关于es6 async函数中reject状态的promise处理

关于es6 async函数中reject状态的promise处理

问题

最近组里的测试去测别的项目组的产品了,所以几个前端页面组里开发人员互相测,在查看一个页签切换的代码时发现了一个以前没注意到的现象:页签使用的elementui的tabs控件,使用before-leave钩子在切换页签前检查当前页签是否有没有填写的表单项在这里插入图片描述
然后代码是这样的:
在这里插入图片描述

在这里插入图片描述
切换到graph页签时需要先检查basic页签是否有没填写的表单项,如果有则不允许切换,测试结果是符合要求的,但代码上看却有疑惑的地方,beforeLeave()方法应该返回一个Promise对象,tabs页签获取这个promise对象后应该是执行类似下面这种逻辑:

promise.then(()=>{
	切换页签
}).catch(()=>{
	不切换页签
})

但是await前并没有加return,async函数是怎么知道返回什么状态的promise?

解析

记得去年看《你不知道的javascript》时,上中下三卷,中卷几乎正本都在讲Promise、迭代器、生成器这块的内容(外国人写书非常详细),现在都忘得差不多了,重新百度了一下,结合《ECMAScript 6 入门》这本书和一些测试,差不多高明白了。

在js代码中,如果使用了promise,且promise最终状态是reject,可以通过promise.catch(fn)捕获,如果没有捕获,f12就会看到类似uncaught xxx之类的错误提示,但和普通异常不同,promise中抛出异常并不会中断代码执行,因为promise是异步的。

async函数总会返回Promise对象,这个是都知道的,比如函数中return true,async会处理成类似return Promise.resovle(true),然后通过then(fn(data))获取的data就是true。

我们知道async和await其实是使用生成器实现的,可以看成基于生成器的语法糖,下面是《ECMAscript6入门》中提供的一个关于语法糖的实现:

async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () {
    // ...
  });
}
function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}

按上述代码分析,step函数第一次调用时,表单校验不通过,await的promise最终为reject状态:

		.catch(() => {
            this.msgError('请检查必填项!')
            return Promise.reject()
          })

所以next.value最终为reject状态的promise,所以执行了step(function() { return gen.throw(e); });然后再次执行step函数,调用gen.throw(e),而异步函数beforeLeave中没有try catch代码块,所以异常会向外层抛出(生成器的throw()方法的特性),也就会在step函数中的此处被捕获:

try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }

也就是说spawn函数最终返回了一个reject状态的promise,tabs页签得知这是一个reject状态的promise,就不会进行切换动作;相反的,当表单校验通过时,await修饰的promise最终是fullfill状态的,此时async生成的语法糖没有捕获到异常,由于代码中也没有返回任何值,所以async最终返回了一个Promise.resolve(),也是一个fullfill状态的promise,但其实和await修饰的那个promise并不是一个,不过只要是fullfill状态的promise,tabs页签都会进行切换。
综上,简单的说就是,如果await修饰的promise最终是reject状态且没有被捕获,async会捕获并返回Promise.reject(error),如果await修饰的promise是fullfill状态则async会return Promise.resolve(),与测试用例期望的情况恰巧是一致的,实际上代码可以简写为:
在这里插入图片描述
再补充一下,上面说的仅适用于await修饰的promise,如果一个promise没有被await修饰(没有被生成器的yield处理),且最终状态为reject,没有被捕,并不会成为async函数的返回值,在f12中可以看到未捕获异常的错误信息,但不会影响他后面的代码执行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值