最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • ES6学习笔记——async

    正文概述 掘金(淰陽)   2021-02-20   572

    含义

    -ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

    async 函数是什么? 一句话,它就是 Generator 函数的语法糖

    一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。

    async函数对 Generator 函数的改进,体现在以下四点。

    (1)内置执行器。

    Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。这完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。

    (2)更好的语义。

    async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

    (3)更广的适用性。

    co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。

    (4)返回值是 Promise。

    async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

    进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

    基本用法

    栗子君来也:

    function timeOut(ms) {
           return new Promise(resolve => {
               setTimeout(resolve, ms);
           })
       }
       async function asyncPrint(value, ms) {
           await timeOut(ms);
           console.log(value);
       }
       asyncPrint('hello world', 5000);
    

    这个例子就是5秒之后在控制台出现hello world。 那如果去掉async函数呢?

    function timeOut(ms) {
           return new Promise(resolve => {
               setTimeout(resolve, ms);
           })
       }
       function asyncPrint(value, ms) {
           timeOut(ms);
           console.log(value);
       }
       asyncPrint('hello world', 5000);
    

    当然是没有延迟5秒就会直接在控制塔输出hello world咯!

    由于async函数返回的是 Promise 对象,可以作为await命令的参数。所以,上面的例子也可以写成下面的形式。

    async function timeOut(ms) {
           return await new Promise(resolve => {
                setTimeout(resolve, ms);
           })
       }
       async function asyncPrint(value, ms) {
           await timeOut(ms);
           console.log(value);
       }
       asyncPrint('hello hui', 5000);
    

    语法

    async函数的语法规则总体上比较简单,难点是错误处理机制。

    返回 Promise 对象

    async函数返回一个 Promise 对象。

    async函数内部return语句返回的值,会成为then方法回调函数的参数。

    async function f() {
           return 'hello world';
       }
       f().then(v => console.log(v));
    

    上面代码中,函数f内部return命令返回的值,会被then方法回调函数接收到。

    async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

    async function f1() {
           throw new Error('error');
       }
       f1().then(
           v => console.log(v),
           e => console.log(e)
       )
    

    Promise 对象的状态变化

    async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

    async function getTitle(url) {
           let response = await fetch(url);
           let html = await response.text();
           return html.match(/<title>([\s\S]+)<\/title>/i)[1];
       }
    getTitle('https://tc39.github.io/ecma262/').then(console.log);
    

    await命令

    正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

    async function f() {
           return await 1234;
       }
       f().then(v => console.log(v));
    

    上面的例子直接返回1234。

    另一种情况是,await命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于 Promise 对象。

    class Sleep {
           constructor(timeout) {
               this.timeout = timeout;
           }
           then(resolve, reject) {
               const startTime = Date.now();
               setTimeout(() => {
                   resolve(Date.now() - startTime),
                       this.timeout
               })
           }
       }
       (async () => {
           const actualTime = await new Sleep(1000);
           console.log(actualTime);
       })();
    

    上面代码中,await命令后面是一个Sleep对象的实例。这个实例不是 Promise 对象,但是因为定义了then方法,await会将其视为Promise处理。

    await命令后面的 Promise对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。

    async function f() {
           await Promise.reject('出错了');
       }
       f().then(v => console.log(v))
           .catch(e => console.log(e))
    

    注意,上面代码中,await语句前面没有return,但是reject方法的参数依然传入了catch方法的回调函数。这里如果在await前面加上return,效果是一样的。

    任何一个await语句后面的 Promise对象变为reject状态,那么整个async函数都会中断执行。

    async function f() {
           await Promise.reject('出错了111');
           await Promise.resolve('hello world');
    }
    f();
    

    上例中的第二个语句不会执行,因为因为第一个await语句状态变成了reject。

    有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。

    async function f() {
           try {
               await Promise.reject('出错了123');
           }  catch (e) {
               return await Promise.resolve('hello world')
           }
    }
    f().then(v => console.log(v));
    

    另一种方法是await后面的 Promise 对象再跟一个catch方法,处理前面可能出现的错误。

    async function f() {
           await Promise.reject('出错了2333').catch(e => console.log(e));
           return await Promise.resolve('hello world');
    }
    f().then(v => console.log(v));
    

    使用注意点

    1. 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

    下面这段代码就是按照顺序执行的(即继发关系):

    function getFoo() {
       return new Promise(() => {
           setTimeout(() => {
               console.log('getFoo');
               console.log(new Date());
           }, 2000);
       });
    }
    function getBar() {
       return new Promise(() => {
           setTimeout(() => {
               console.log('getBar');
               console.log(new Date());
           }, 2000);
       });
    }
    async function f() {
       // 按顺序运行
       var foo = await getFoo();
       var bar = await getBar();
    }
    f();
    

    函数运行后2s,输出'getFoo',再2s后输出'getBar'。

    以下两种写法,getFoo和getBar都是同时触发,这样就会缩短程序的执行时间。

    function getFoo() {
       return new Promise(() => {
           setTimeout(() => {
               console.log('getFoo');
               console.log(new Date());
           }, 2000);
       });
    }
    function getBar() {
       return new Promise(() => {
           setTimeout(() => {
               console.log('getBar');
               console.log(new Date());
           }, 2000);
       });
    }
    async function f1() {
       // 同时触发写法一
       // let [foo, bar] = await Promise.all([getFoo(), getBar()]);
    
       // 同时触发写法二
       let fooPromise = getFoo();
       let barPromise = getBar();
       let foo = await fooPromise;
       let bar = await barPromise;
    }
    f();
    

    以上两种写法,2s后,'getFoo'和'getBar'同时输出。

    2. await命令只能用在async函数之中,如果用在普通函数,就会报错。

    但是如果将forEach方法的参数改成async函数,也有问题。

    function f() {
      return new Promise(resolve => {
          setTimeout(() => {
              resolve(console.log(new Date().getSeconds()))
          }, 1000);
      });
    }
    
    function f1() {
       let docs = [{}, {}, {}];
       docs.forEach(async function(doc) {
           await f(doc);
       });
    }
    f1();   // 并发执行
    // 33 33 33(33s时运行,得到三个33)
    

    上面代码结果为三个相同的值,原因是这时三个f(doc)操作是并发执行,也就是同时执行,而不是继发执行。正确的写法是采用for循环。

    function f() {
      return new Promise(resolve => {
          setTimeout(() => {
              resolve(console.log(new Date().getSeconds()))
          }, 1000);
      });
    }
    
    async function f2() {
       let docs = [{}, {}, {}];
       for (let doc of docs) {
           await f(doc);
       }
    }
    f2();  // 继发执行
    // 33 34 35 (33s时运行,得到三个不同的值)
    

    最后,留一个小测试:

    async function async1() {
      console.log('async1 start');
      await async2();
      console.log('async1 end');
    }
    async function async2() {
      console.log('async2');
    }
    console.log('script start');
    setTimeout(() => {
      console.log('setTimeout');
    }, 0);
    async1();
    new Promise(resolve => {
          console.log('promise1');
          resolve();
    }).then(() => {
      console.log('promise2');
    });
    console.log('script end');
    

    答案是什么呢?正确答案在下方哟!!

    // script start
    // async1 start
    // async2
    // promise1
    // script end
    // promise2
    // async1 end
    // setTimeout
    

    起源地下载网 » ES6学习笔记——async

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元