出现原因

Promise 太麻烦了

  1. function divPromise (num) {
  2. return new Promise((resolve, reject) => {
  3. $(`div:nth-child(${num})`).fadeIn(750, () => {
  4. resolve()
  5. })
  6. })
  7. }
  8. divPromise(1).then(() => {
  9. return divPromise(2)
  10. }).then(() => {
  11. return divPromise(3)
  12. })
  13. //改造下
  14. async function divLoading () {
  15. for (let i = 1; i <= 3; i++) {
  16. await divPromise(i)
  17. }
  18. }
  19. divLoading()

概念

用于修饰一个函数为异步函数

作用

是用于等待异步操作结束并可以接受到Promise对象中传入到resolve()里的数据

语法

  1. async function(){
  2. let 变量名 = await promise对象;
  3. }

特点

  • 异步函数的返回值是一个promise对象,会将该函数return 后的数据作为resolve函数的实际参数

    1. async function 函数名(){
    2. return 数据;// 该数据会作为返回的promise对象(resolve()的实际参数)
    3. }
    4. 函数名().then((data) => {
    5. console.log(data); //data就是上面return的数据
    6. })
  • 可以额外使用await关键字,普通函数中不能使用await

  • 异步函数内部可以写任意代码,不仅仅是异步代码

    async

    执行流程

    普通执行流程(同步)

    异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行。且异步函数内部可以写任意代码,不仅仅是异步代码
    1. console.log(1);
    2. async function demo () {
    3. console.log(2);
    4. setTimeout(() => {
    5. console.log(3);
    6. }, 100);
    7. console.log(4);
    8. }
    9. demo();
    10. console.log(5);
    11. //1 2 4 5 3

    有返回值的执行流程

    1、return 不是Promise对象

    image.png
    返回值一定是Promise,会被包裹到Promise.resolve( )中,因此可以直接接.then(res),res就是return 的结果(没有就是undefined) ```javascript const promise = foo() promise.then(res => {

})

  1. <a name="Cxf36"></a>
  2. #### 2、return 实现then的对象
  3. return 的是一个对象,并且实现了then方法,这样就会直接执行这个then方法,然后把结果传递到后面的then<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/12695178/1653197286532-affb77d7-40b0-4a90-846a-91aae485c2d1.png#clientId=u50418392-0eaf-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=283&id=ua48cc3e8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=354&originWidth=430&originalType=binary&ratio=1&rotation=0&showTitle=false&size=24526&status=done&style=none&taskId=ua580f95d-d0d3-42a8-aea9-74eb780d993&title=&width=344)
  4. <a name="f3jCH"></a>
  5. #### 3、return Promise对象
  6. 如果我们的异步函数的返回值是Promise 对象,那么后面Promise的状态会由这个Promise对象决定,是执行then还是执行catch
  7. <a name="Amxrw"></a>
  8. ### 抛出异常的执行流程
  9. 在普通函数中,当运行到 throw 抛出异常,就会报错并终止整个程序继续运行;
  10. 而async 异步函数,运行到 throw 抛出异常,不会报错,不会终止整个程序继续运行,而是会作为Promise的reject来传递,后面可以用(必须要用)catch来捕获异常<br />![](https://cdn.nlark.com/yuque/0/2021/png/725902/1638973050445-54db47bb-67f7-430e-8bc0-8d3ab7c4d28d.png?x-oss-process=image%2Fresize%2Cw_631%2Climit_0#crop=0&crop=0&crop=1&crop=1&from=url&id=l3WSQ&margin=%5Bobject%20Object%5D&originHeight=470&originWidth=631&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
  11. <a name="zUCXa"></a>
  12. # await
  13. 只能用在异步函数 async 里面
  14. <a name="udg18"></a>
  15. ## 1、后面跟 成功的Promise
  16. - await关键字的特点
  17. - 通常使用await是后面会跟上一个表达式,这个表达式会返回一个Promise ;
  18. - 那么await会等到Promise的状态变成fulfilled状态,之后继续执行异步函数;
  19. ![](https://cdn.nlark.com/yuque/0/2021/png/725902/1638973587470-4127b309-b0ec-499c-8afd-35d53b449791.png?x-oss-process=image%2Fresize%2Cw_825%2Climit_0#crop=0&crop=0&crop=1&crop=1&from=url&id=pFQeT&margin=%5Bobject%20Object%5D&originHeight=307&originWidth=825&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
  20. - 也可以直接写new Promise
  21. ```javascript
  22. async function foo () {
  23. const res = await new Promise(resolve => {
  24. resolve('res')
  25. })
  26. console.log(res);
  27. }
  28. foo() //'res'

2、后面跟 普通的值

如果await后面是一个普通的值,那么会直接返回这个值;

  1. async function foo () {
  2. const res = await 123
  3. console.log(res);
  4. }
  5. foo() //123

3、后面跟 实现then的对象

如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值;
image.png

4、后面跟 失败的Promise

会直接跳出运行,把结果返回给外面(且当前异步函数后面不执行了),需要用catch接收
async%26await - 图3
也可以通过try catch语句捕获
async%26await - 图4

限制

  1. // 不允许:await 出现在箭头函数中
  2. function foo() {
  3. const syncFn = () => {
  4. return await Promise.resolve('foo');
  5. };
  6. console.log(syncFn());
  7. }
  8. // 不允许:await 出现在同步函数表达式中
  9. function baz() {
  10. const syncFn = function() {
  11. return await Promise.resolve('baz');
  12. };
  13. console.log(syncFn());
  14. }

停止和恢复执行

JavaScript 运行时在碰到 await 关键字时,会记录在哪里暂停执行。
等到 await 右边的值可用了,JavaScript 运行时会向消息队列中推送一个任务,这个任务会恢复异步函数的执行。

  1. async function foo() {
  2. console.log(await Promise.resolve('foo'));
  3. }
  4. async function bar() {
  5. console.log(await 'bar');
  6. }
  7. async function baz() {
  8. console.log('baz');
  9. }
  10. foo();
  11. bar();
  12. baz();
  13. // baz
  14. // bar
  15. // foo
  1. async function foo() {
  2. console.log(2);
  3. console.log(await Promise.resolve(8));
  4. console.log(9);
  5. }
  6. async function bar() {
  7. console.log(4);
  8. console.log(await 6);
  9. console.log(7);
  10. }
  11. console.log(1);
  12. foo();
  13. console.log(3);
  14. bar();
  15. console.log(5);
  16. // 1 2 3 4 5 8 9 6 7

顺序执行、并行执行

  1. // 定义一个延迟执行
  2. async function randomDelay(id) {
  3. // 延迟 0~1000 毫秒
  4. const delay = Math.random() * 1000;
  5. return new Promise((resolve) => setTimeout(() => {
  6. console.log(`${id} finished`);
  7. resolve();
  8. }, delay));
  9. }
  10. // 顺序执行
  11. async function foo() {
  12. const t0 = Date.now();
  13. await randomDelay(0);
  14. await randomDelay(1);
  15. await randomDelay(2);
  16. await randomDelay(3);
  17. await randomDelay(4);
  18. console.log(`${Date.now() - t0}ms elapsed`);
  19. }
  20. foo();
  21. // 0 finished
  22. // 1 finished
  23. // 2 finished
  24. // 3 finished
  25. // 4 finished
  26. // 877ms elapsed
  27. // 并行执行
  28. async function foo() {
  29. const t0 = Date.now();
  30. const promises = Array(5).fill(null).map((item, index) => randomDelay(index));
  31. for (const p of promises) {
  32. console.log(`awaited ${await p}`);
  33. }
  34. /*相当于
  35. const p0 = randomDelay(0);
  36. const p1 = randomDelay(1);
  37. const p2 = randomDelay(2);
  38. const p3 = randomDelay(3);
  39. const p4 = randomDelay(4);
  40. await p0;
  41. await p1;
  42. await p2;
  43. await p3;
  44. await p4;
  45. */
  46. console.log(`${Date.now() - t0}ms elapsed`);
  47. }
  48. foo();
  49. // 1 finished
  50. // 2 finished
  51. // 4 finished
  52. // 3 finished
  53. // 0 finished
  54. // awaited 0
  55. // awaited 1
  56. // awaited 2
  57. // awaited 3
  58. // awaited 4
  59. // 645ms elapsed

性能

Promise与异步函数的功能有相当程度的重叠,但它们在内存中的表示则差别很大。
Promise:
JavaScript 引擎会在创建Promise时尽可能保留完整的调用栈。
在抛出错误时,调用栈可以由运行时的错误处理逻辑获取,因而就会出现在栈追踪信息中。
当然,这意味着栈追踪信息会占用内存,从而带来一些计算和存储成本。

异步函数async/await
JavaScript 运行时可以简单地在嵌套函数中存储指向包含函数的指针,就跟对待同步函数调用栈一样。
这个指针实际上存储在内存中,可用于在出错时生成栈追踪信息。
这样就不会像之前的例子那样带来额外的消耗,因此在重视性能的应用中是可以优先考虑的。

实例:Ajax请求

  1. //根据url生成promise
  2. function ajaxPromise (url) {
  3. return new Promise(function (resolve) {
  4. $.ajax({
  5. url,
  6. success: function (data) {
  7. resolve(data)
  8. }
  9. })
  10. })
  11. }

promise then 方法

  1. ajaxPromise(url).then(data => {
  2. //拿到data数据
  3. })

async await 方法

  1. let data = await ajaxPromise(url);
  2. console.log(count);