async函数

  1. async是什么?它和Genernator函数外观上的区别是什么?

    const asyncReadFile = async function(){
        const f1 = await readFile(url1)
        const f2 = await readFile(url2)
    }

    async是Genernator函数的语法糖。

    asyncGenernator的区别有两点:

    • Genernator*变成async
    • yield变成 await
  2. asyncGenernator做了哪些改进?

    • 内置执行器。

      执行时和普通函数一样,只需要调用函数就完了,不需要 next()方法

    • 更好的语义。

      async表示它后面的函数里有异步操作;await表示紧跟在后面的表达式需要等待结果

    • 更广的适用性。

      await后面可以Genernator, Promise对象和原始类型的值。(原始类型的值会被自动转化为resolved状态的Promise对象)

    • 返回值是Promise

      async函数返回的是Promise对象,方便用 then进行下一步操作

    • 总的来说:async函数可看成多个异步操作,包装成一个Promise对象,而await命令就是内部then命令的语法糖。
  3. async执行的例子:

    function timeout(ms){
      return new Promise((resolve) => {
        setTimeout(resolve, ms)
      })
    }
    async function asyncPrint(value, ms){
      await timeout(ms);
      console.log(value)
    }
    
    asyncPrint("你好啊", 5000)

    5s之后打印 “你好啊”

    以上 asyncPrint的意思是,等待timeout函数执行完了之后,才会继续执行 console.log(value)

  4. async函数的几种使用场景?

    五种场景下:

    • 函数声明

      async function foo(){}
    • 函数表达式

      const foo = async function(){}
    • 箭头函数

      const foo = async () => {}
    • 对象的方法

      let obj = {
       async foo(){
      
           }
      }
      obj.foo().then(...)
    • class方法

      class Storage{
       constructor(){
              this.cachePromise = caches.open("avatars")
          }
          async getAvatar(name){
              const cache = await this.cachePromise;
              return cache.match()
          }
      }
      const storage  = new Storage()
      storage.getAvatar("joy").then(...)
  5. async函数返回什么?

    返回一个 Promise对象。

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

    async函数内部抛出的错误,会导致返回的Promise对象变为 rejected状态

  6. `async函数中Promise对象是如何变化的?

    async函数返回的是Promise对象P。必须等到内部 await命令的Promise对象执行完后,P才会发生状态改变,除非遇到return语句,或者抛出了错误。

    换言之,async函数 内部的异步操作执行完了,才会执行调用它时后面then方法指定的回调函数。

  7. await命令的返回值是什么?

    • 正常情况下,await后面是Promise对象,会返回此对象的结果;如果不是Promise对象,直接返回对应的值
    • await命令后面跟了 thenable对象,会把 thenable对象当做 Promise对象来处理
  8. await后面的Promise对象如果变为 rejected会怎样?

    • rejected的参数会被async函数catch方法的回调函数接收到。

      async function f() {
        await Promise.reject('出错了');
      }
      
      f()
      .then(v => console.log(v))
      .catch(e => console.log(e))
    • 任何一个await语句的Promise对象变为reject状态,整个async函数都会被中断

      async function f() {
        await Promise.reject('出错了');
        await Promise.resolve('hello world'); // 不会执行
      }
  9. 如果希望前一个异步操作失败,不中断后面的异步操作,怎么处理?

    两种方法:

    • 把第一个await放到 try...catch里面

      async function f(){
          try{
              await Promise.reject("出错了")
          }catch(e){}
          return await Promise.resolve("hello world")
      }
      f().then(v =>console.log(v)).catch(e => console.log(e))
      // 打印的是  hello world
    • await后面的Promise对象再跟一个 catch方法,处理前面可能出现的错误

      async function f(){
        await Promise.reject("出错啦").catch(e => {
          console.log("await 内部promise被reject了")
        })
        return await Promise.resolve("hello world");
      }
      f().then(v => console.log(v)).catch(e => console.log(e))

      打印的内容是:

      await 内部promise被reject了
      hello world

  10. await后面的异步操作出错了(例如某行代码throw 了一个Error),是什么意思?

    async函数返回的Promise对象被reject

    async function f() {
      await new Promise(function (resolve, reject) {
        throw new Error('出错了');
      });
    }
    
    f()
    .then(v => console.log(v))
    .catch(e => console.log(e)) // catch执行了, e就是抛出的错误对象 new Error('出错了')
  11. 如何防止出错呢?

    还是将其放到try{ }catch(e){ }代码块中。

    如果有多个await命令,可以将其统一放到try{ }catch(e){ }结构里。
    下面是实际例子: 多次发起客户端请求,如果请求成功,跳出循环往下执行;如果不成功,继续请求,直到达到最大数目 NUM_RETRIES

    async function test(){
      let i;
      for(i = 0; i < NUM_RETRIES; ++i){
        try{
          await superagent.get(url)
          break;
        }catch(err){}
      }
      console.log(i)
    }
    test()

    如果await操作成功,则会break,跳出for循环;如果await操作不成功,则会被catch住,然后继续下一轮for循环,直到超过 NUM_RETRIES或者 await操作成功。

  12. asyncawait有哪些使用上注意的点?

    • await命令后的Promise对象可能reject,因此await命令最好放在try{ }catch(e){ }代码块中

      async function myFunction() {
        try {
          await somethingThatReturnsAPromise();
        } catch (err) {
          console.log(err);
        }
      }
      
      // 另一种写法
      
      async function myFunction() {
        await somethingThatReturnsAPromise()
        .catch(function (err) {
          console.log(err);
        });
      }
    • 多个await异步操作时,如果不存在继发关系,让它们同时触发比较好

      可以结合 Promise.all方法

      // 写法一
      let [foo, bar] = await Promise.all([getFoo(), getBar()]);
      
      // 写法二
      let fooPromise = getFoo();
      let barPromise = getBar();
      let foo = await fooPromise;
      let bar = await barPromise;
    • await命令只能用在async函数中,用在普通函数里会报错

      注意forEach的回调函数,await也不能出现在回调函数里

    • async函数可以保留运行堆栈

      看例子:

      const a = () => {
          b().then(() => c()) ;
      }
      
      const a = async () => {
          await b();
          c();
      }

      上面的例子中,b运行时,a可能已经执行完了。如果此时b或c报错,错误堆栈将不包括a

      下面例子中,b运行时,a只是暂停,若此时b或者c报错了,错误堆栈中将包括a

  13. Promise写法和Genernator写法,async有什么好处?

    Promise写法有很多catchthen,语义性不强。

    Genernator函数需要有一个任务运行器,自动执行Genernator函数,并且 yield后面的表达式,必须返回Promise对象

    async最简洁,最符合语义,将Genernator写法的自动执行器,改在 语言层面提供,不暴露给用户,代码量最少。

    async function chainAnimationAsync(elem, ainmations){
        let ret = null
        try{
            for(letanim of animations){
                ret = await anim(elem);
            }
        }catch(e){}
        return ret
    }
  14. async的实例: 按顺序完成异步操作——依次远程读取一组URL,然后按照读取顺序输出结果

    async function logInOrder(urls){
      const textPromises = urls.map(async url => {
        const response = await fetch(url)
        return response.text()
      })
      for(const textPromise of textPromises){
        console.log(await textPromise)
      }
    }

    map的参数是async函数。这几个async是并发的。只有async函数内部才是继发的【const response = await fetch(url)return response.text() 先执行】,外部并不受影响。

    后面在 for...of循环内部使用了 await,这几个await是顺序执行。


joychenke
47 声望3 粉丝

业精于勤荒于嬉。加油ヾ(◍°∇°◍)ノ゙