2

前言

网上有很多关于async/await的学习文章,我也是通过这些文章学习了解async的,但是不总结一下,总觉得没有真正学到。如有错误的地方还请多多指出。

什么是async/await

有人说它是JS编程异步的“终极解决方案”,终不终极不知道,事物总是在发展的嘛,但是能被冠上这么个头衔也绝非等闲之辈了?。async是“异步的”,await是“等待”,async是形容词,await是动词,那么async形容的函数告诉别人这是个包含异步的“异步的函数”,而await要等待某个东西的到来。a/a给了我们重新使用try catch的权力,并且规定await必须写在async形容的函数中。使用a/a要对Promise有所理解,那还要从回调说起。

回调函数

JS是一根筋的(单线程),一次只执行一个任务,后面的要等前面的完成才行, 如果某个任务要占用很长时间,那么后面的任务就都要等着。所以

JS将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。地址

回调函数就是“异步“的方法之一。大家对回调很熟啦,回调会产生一个问题-回调地狱,两层回调还好,三层似乎也可以接受,硬着头皮四层也行,五层要不要写,咬咬牙六层就好了。。。最后,一首杨宗纬的洋葱送给自己。一层一层剥开回调,找到最终的小可爱。
比如下面的栗子:

a1(function(res) {
  d2(res, function(newRes) {
    a3(newRes, function(finalRes) {
      console.log('a3: ' + finalRes);
    }, a1FCb);
  }, a2FailCb);
}, a3FailCb);

另外,当年我还年轻的时候,初试回调,在回调函数中花样百出的尝试return,在回调外面也取不到return的值,大概下面这个样子

var a = ''
function fool(){
  setTimeout(() => {
     return 'hi'// 或者
  }, 1000);
}
a = fool()
console.log(a) //undefined 因为setTimeout异步执行,a=fool()执行在timeout之前,而此时的fool()并内有return任何东西

回调让我们失去了直接“使用return”的方式,那来看看Promise。

Promise

顾名思义,promise是承诺的意思,承诺在未来是可以兑现的,promise可以有效的解决回调地狱,而且给了我们使用return的权力。只不过它的return仍是一个承诺。对于promise的学习可参考promise MDNPromise MDN

一个 Promise有以下几种状态:
pending: 初始状态,既不是成功,也不是失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。

如下一个利用Promise构造函数声明的实例

  new Promise(function(resolve,reject){//...})

resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败),这两个状态不能同时出现,两个状态完成时,分别调用promise的then方法中对应的onfulfilled 和onrejected 函数。举个例子:

function a(val){
  return new Promise(function(resolve,reject){
    if(val === 1){
      resolve(val)
    } else {
      reject('bad val')
    }
  })  
}

函数a接收一个参数,返回一个promise实例,当参数是期望的1时,表示操作成功,其他值被认为操作失败。那么如果执行以下操作

a(1).then(function(res){
  console.log('onfulfilled '+ res)
},function(errMsg){
  console.log('onrejected '+ errMsg)
})
//会log出'onfulfilled 1'

该操作a(1)会调用函数a内部promise实例的resolve函数,把该promise的状态变成了fulfilled,所以之后会调用then函数里onfulfilled的函数。

另外,对于操作失败的状态,除了在then中指定对应的onreject函数外,也可以使用catch,使用catch的好处在于,如果是多个异步操作,只需要指定一个catch即可,不用为每层then都指定一个onreject函数。举个例子:

有如下三层回调:

fun1(function(res1){
  fun2(function(res1){
    var res2 = res1 + 'hello'
    fun3(function(res2){
      var res3 = res2 + 'hi'
      console.log('the final res ' + res3)
    },fun3FailCb)
  },fun2FailCb)
},fun1FailCb)

改成promise写法后:

fun1()
  .then(function(res1){
    return fun2(res1+'hello')
  })
  .then(function(res2){
    return fun3(res2+'hi')
  })
  .then(function(res3){
    console.log('the final res' + res3)
  })
  .catch(function(err){
    console.log('something bad occured')
  }) 

清爽很多。then的链写多了,看起来也很烦啊,有没有更简洁的方法?有!

async/await

async/await是一个语法糖,他和promise关系紧密,可参考async MDN

当调用一个 async 函数时,会返回一个 Promise 对象。当这个 async 函数返回一个值时,Promise 的 resolve 方法会负责传递这个值;当 async 函数抛出异常时,Promise 的 reject 方法也会传递这个异常值。

回到开篇说的,async所形容的函数告诉别人这是个包含异步的“异步的函数”,也就是async的函数会返回一个promise (同步的代码也没关系,只不过没啥意义了)。await要等的就是其他async函数返回的这个promise,promise状态成功,await等到的是resolve中的值,promise状态失败,await等到的是reject中的错误信息,换句话说,被try/catch中的catch到了。举个例子:

async function f1(val){
  if(val === 1){
    return val
  } else {
    throw 'not 1'
  }
} //调用f1(val)时,会自动返回一个包装好的promise
async function f2(val2){
 try{
   var res1 = await f1(val2)
   console.log('res1' , res1)
  }catch(errmsg){
   console.log('catch ', errmsg)
  }  
}

分别执行f2(1)和f2(3)查看log结果。

改写前面的三层回调

async function fun1(){
  return res1
}
async function fun2(val){
  return val + 'hello'
}

async function fun3(val){
  return val + 'hi'
}

async function fun4(){
 try{
   var res1 = await fun1()
   var res2 = await fun2(res1)
   var res3 = await fun3(res2)
   console.log('the final res ', res3)
   // other operation with res3/res2/res1...
 }catch(err){
   console.log('something bad')
 }
  
}

相当清爽。在async内,await会阻塞后面的程序执行,直到promise的状态完成(成功或失败)。


TAG_WW
55 声望6 粉丝

龟速前行的ZZ