异步Promise及Async/Await详解

一、为什么有Async/Await?

我们都知道已经有了Promise的解决方案了,为什么还要ES7提出新的Async/Await标准呢?

答案其实也显而易见:Promise虽然跳出了异步嵌套的怪圈,用链式表达更加清晰,但是我们也发现如果有大量的异步请求的时候,流程复杂的情况下,会发现充满了屏幕的then,看起来非常吃力,而ES7的Async/Await的出现就是为了解决这种复杂的情况。

首先,我们必须了解Promise

二、Promise简介

2.1 Promise实例

什么是Promise,很多人应该都知道基础概念?直接看下面的代码(全文的例子都是基于setDelaySecondsetDelay两个函数,请务必记住):

const setDelay = (millisecond) => {
   
  return new Promise((resolve, reject)=>{
   
      if (typeof millisecond != 'number') reject(new Error('参数必须是number类型'));
      setTimeout(()=> {
   
        resolve(`我延迟了${
     millisecond}毫秒后输出的`)
      }, millisecond)
  })
}

我们把一个Promise封装在一个函数里面同时返回了一个Promise,这样比较规范。

可以看到定义的Promise有两个参数,resolvereject

  • resolve:将异步的执行从pending(请求)变成了resolve(成功返回),是个函数执行返回。
  • reject:顾名思义“拒绝”,就是从请求变成了"失败",是个函数可以执行返回一个结果,但我们这里推荐大家返回一个错误new Error()


上述例子,你可以reject('返回一个字符串'),随便你返回,但是我们还是建议返回一个Error对象,这样更加清晰是“失败的”,这样更规范一点。

2.2 Promise的then和catch

我们通过Promise的原型方法then拿到我们的返回值:

setDelay(3000)
.then((result)=>{
   
    console.log(result) // 输出“我延迟了2000毫秒后输出的”
})

输出下列的值:“我延迟了2000毫秒后输出的”。

如果出错呢?那就用catch捕获:

setDelay('我是字符串')
.then((result)=>{
   
    console.log(result) // 不进去了
})
.catch((err)=>{
   
    console.log(err) // 输出错误:“参数必须是number类型”
})

是不是很简单?好,现在我增加一点难度,如果多个Promise执行会是怎么样呢?

2.3 Promise相互依赖

我们在写一个Promise

const setDelaySecond = (seconds) => {
   
  return new Promise((resolve, reject)=>{
   
      if (typeof seconds != 'number' || seconds > 10) reject(new Error('参数必须是number类型,并且小于等于10'));
      setTimeout(()=> {
   
        console.log(`先是setDelaySeconds函数输出,延迟了${
     seconds}秒,一共需要延迟${
     seconds+2}`)
        resolve(setDelay(2000)) // 这里依赖上一个Promise
      }, seconds * 1000)
  })
}

在下一个需要依赖的resolve去返回另一个Promise,会发生什么呢?我们执行一下:

setDelaySecond(3).then((result)=>{
   
  console.log(result)
}).catch((err)=>{
   
  console.log(err);
})

你会发现结果是先执行:“先是setDelaySeconds输出,延迟了2秒,一共需要延迟5秒”

再执行setDelayresolve:“我延迟了2000毫秒后输出的”。的确做到了依次执行的目的。

有人说,我不想耦合性这么高,想先执行setDelay函数再执行setDelaySecond,但不想用上面那种写法,可以吗,答案是当然可以。

2.4 Promise链式写法

先改写一下setDelaySecond,拒绝依赖,降低耦合性

const setDelaySecond = (seconds) => {
   
  return new Promise((resolve, reject)=>{
   
      if (typeof seconds != 'number' || seconds > 10) reject(new Error('参数必须是number类型,并且小于等于10'));
      setTimeout(()=> {
   
        resolve(`我延迟了${
     seconds}秒后输出的,是第二个函数`)
      }, seconds * 1000)
  })
}

先执行setDelay在执行setDelaySecond,只需要在第一个then的结果中返回下一个Promise就可以一直链式写下去了,相当于依次执行:

setDelay(2000)
.then((result)=>{
   
  console.log(result)
  console.log('我进行到第一步的');
  return setDelaySecond(3)
})
.then((result)=>{
   
  console.log('我进行到第二步的');
  console.log(result);
}).catch((err)=>{
   
  console.log(err);
})

发现确实达到了可喜的链式(终于脱离异步嵌套苦海,哭),可以看到then的链式写法非常优美。

2.5 链式写法需要注意的地方

这里一定要提到一点:

then式链式写法的本质其实是一直往下传递返回一个新的Promise,也就是说then在下一步接收的是上一步返回的Promise,理解这个对于后面的细节非常重要!!

那么并不是这么简单,then的返回我们可以看出有2个参数(都是回调):

第一个回调是resolve的回调,也就是第一个参数用得最多,拿到的是上一步的Promise成功resolve的值。
第二个回调是reject的回调,用的不多,但是求求大家不要写错了,通常是拿到上一个的错误,那么这个错误处理和catch有什么区别和需要注意的地方呢?
我们修改上面的代码:

setDelay(2000)
.then((result)=>{
   
  console.log(result)
  console.log('我进行到第一步的');
  return setDelaySecond(20)
})
.then((result)=>{
   
  console.log('我进行到第二步的');
  console.log(result);
}, (_err)=> {
   
  console.log('我出错啦,进到这里捕获错误,但是不经过catch了');
})
.then((result)=>{
   
  console.log('我还是继续执行的!!!!')
})
.catch((err)=>{
   
  console.log(err);
})

可以看到输出结果是:进到了then的第二个参数(reject)中去了,而且最重要的是!不再经过catch了。

那么我们把catch挪上去,写到then错误处理前:

setDelay(2000)
.then((result)=>{
   
  console.log(result)
  console.log('我进行到第一步的');
  return setDelaySecond(20)
})
.catch((err)=>{
    // 挪上去了
  console.log(err); // 这里catch到上一个返回Promise的错误
})
.then((result)=>{
   
  console.log('我进行到第二步的');
  console.log(result);
}, (_err)=> {
   
  console.log('我出错啦,但是由于catch在我前面,所以错误早就被捕获了,我这没有错误了');
})
.then((result)=>{
   
  console.log('我还是继续执行的!!!!')
})

可以看到先经过catch的捕获,后面就没错误了。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值