美文网首页
笔记:Javascript中的Async与Await

笔记:Javascript中的Async与Await

作者: chenhong_f1e2 | 来源:发表于2019-04-13 13:12 被阅读0次

    1. 概述

    ES2017(ES8)的新特性,允许我们用同步的方式编写异步代码。async / await建立在promises之上。

    2. 例子

    例1:

    function scaryClown() {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve('🤡');
        }, 2000);
      });
    }
    
    async function msg() {
      //等待一个Promise处理完(执行resolve或reject)
      const msg = await scaryClown(); 
      console.log('Message:', msg);  
    }
    
    msg(); // Message: 🤡
    

    例2:

    function resolveAfter2Seconds() {
      return new Promise(resolve => {
        console.log('promsie')
        setTimeout(() => {
          resolve('resolved');
        }, 2000);
      });
    }
    
    async function asyncCall() {
      console.log('calling');
      var result = await resolveAfter2Seconds();
      console.log(result);
      // expected output: 'resolved'
    }
    
    asyncCall();
    /*
    输出:
    calling
    promise
    resolved
    */
    
    

    await关键字会阻塞函数并等待一个Promise处理完(执行resolve或reject),然后才执行之后的语句,该关键字只能在async修饰的函数中使用。阻塞过程中不会占用任何CPU资源,因为引擎可以同时执行其他任务:执行其他脚本,处理事件等。

    稍微复杂一点:

    例3:

    function who() {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve('🤡');
        }, 200);
      });
    }
    
    function what() {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve('lurks');
        }, 300);
      });
    }
    
    function where() {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve('in the shadows');
        }, 500);
      });
    }
    
    async function msg() {
      //a,b,c会串行执行
      //0.2秒后返回
      const a = await who();
      //0.2+0.3=0.5秒后返回
      const b = await what();
      //0.2+0.3+0.5=1秒后返回
      const c = await where();
      console.log(`${ a } ${ b } ${ c }`);
    }
    
    msg()  //🤡 lurks in the shadows
    

    上述的例,三个异步函数会串行执行。 如果想要三个异步函数并行执行,执行完之后再输出,可以使用Promise.all()函数:

    async function msg() {
      //a,b,c并行执行, 0.5秒后返回: max(0.2,0.3,0.5)
      const [a, b, c] = await Promise.all([who(), what(), where()]);
      console.log(`${ a } ${ b } ${ c }`);
    }
    
    msg();   //🤡 lurks in the shadows
    

    3. 使用async的注意点

    async修饰的函数返回的永远是一个Promise ,例如:

    
    async function hello() {
      //返回的将是一个Promsie对象,而不是字符串
      return 'Hello Alligator!';
    }
    
    const b = hello();
    
    console.log(b);  //Promise {<resolved>: "Hello Alligator!"}
    
    

    因此,如果想要获取返回的字符串,需要这样:

    
    async function hello() {
      return 'Hello Alligator!';
    }
    
    hello().then(x => console.log(x)); // Hello Alligator!
    

    如果async修饰的函数没有返回值,它会返回一个Promise.resolve(undefined)

    async function hello() {
      let x 
    }
    
    hello().then(x => console.log(x));  // undefined
    

    4. 异步执行的时间点

    async/await 与 promise相同,永远都是异步的(即使立马resolve)。当一个promise被处理完,它的 .then/catch/finally 处理方法会被压入一个待执行的队列(microtask queue), 当Javascript引擎从当前代码中解脱出来时,Javascript引擎从该队列中获取任务并执行它。如下代码所示,alert永远先执行:

    let promise = Promise.resolve();
    
    promise.then(() => alert("promise done"));
    
    alert("code finished"); // this alert shows first
    

    Javascript中除了microtask queue还有另外一个异步队列 macrotask queue用来处理事件(比如鼠标点击)。microtask拥有比macrotask更高的优先级,因此JavaScript引擎会先执行microtask queue中的任务,执行完之后才会执行macrotask queue里面的任务。如下代码所示:

    setTimeout(() => alert("timeout"), 0);
    
    Promise.resolve()
      .then(() => alert("promise"));
    
    alert("code");
    
    1. code先出现
    2. promise随后出现
    3. timeout最后出现

    例2:

    Promise.resolve()
      .then(() => {
        setTimeout(() => alert("timeout"), 0);
      })
      .then(() => {
        alert("promise");
      });
    
    1. Promise.resolve()执行完成
    2. setTimeout将事件压入了 macrotask queue队列
    3. 此时microtask queue 还有任务,因此执行alert("promise")
    4. 最后执行macrotask queue中的任务,alert("timeout")

    总而言之: 当前代码执行完成 -> microtask queue执行 -> macrotask queue执行

    5. await的行为

    await后面是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值。

    1. 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
    2. 如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。

    这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。

    现在有一个问题,await后面如果不是Promise,它还会阻塞吗?

    很多人以为await会一直等待之后的表达式执行完之后才会继续执行后面的代码,实际上await是一个让出线程的标志。await后面的函数会先执行一遍,然后就会跳出整个async函数来执行后面js栈(后面会详述)的代码。等本轮事件循环执行完了之后又会跳回到async函数中等待await
    后面表达式的返回值,如果返回值为非promise则继续执行async函数后面的代码,否则将返回的promise放入promise队列(Promise的Job Queue)

    例:

    function testSometing() {
        console.log("执行testSometing");
        return "testSometing";
    }
    
    async function testAsync() {
        console.log("执行testAsync");
        return Promise.resolve("hello async");
    }
    
    async function test() {
        console.log("test start...");
        const v1 = await testSometing();//关键点1
        console.log(v1);
        const v2 = await testAsync();
        console.log(v2);
        console.log(v1, v2);
    }
    
    test();
    
    var promise = new Promise((resolve)=> { console.log("promise start.."); resolve("promise");});//关键点2
    promise.then((val)=> console.log(val));
    
    console.log("test end...")
    

    输出:

    test start...
    执行testSometing
    promise start..
    test end...
    testSometing
    执行testAsync
    promise
    hello async
    testSometing hello async
    

    本文摘录及参考自:

    1. Exploring Async/Await Functions in JavaScript
    2. Async/await - The Modern Javascript Tutorial
    3. Microtasks and event loop
    4. async function - JavaScript | MDN - Mozilla
    5. 理解JavaScriptasync/await - 边城客栈- SegmentFault 思否
    6. Modern Asynchronous JavaScript with Asyncand Await - Flavio Copes
    7. 深入理解 JavaScript 异步系列(5)—— async await- 王..._博客园
    8. JavaScript* async / await:好处、坑和正确用法 - yexun..._CSDN博客
    9. Javascript中的async await - cpselvis - 博客园
    10. async/await 执行顺序详解- 前端学习日记- SegmentFault 思否

    相关文章

      网友评论

          本文标题:笔记:Javascript中的Async与Await

          本文链接:https://www.haomeiwen.com/subject/ufafvqtx.html