关于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中可以看到未捕获异常的错误信息,但不会影响他后面的代码执行。

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

  1. es6 async函数的异步迭代器

    async函数的异步迭代器 <迭代器>一章说过,Iterator 接口是一种数据遍历的协议,只要调用迭代器对象的next方法,就会得到一个对象,表示当前遍历指针所在的那个位置的信息.nex ...

  2. es6 async函数的语法

    async函数的语法 async函数的语法规则总体上比较简单,难点是错误处理机制. 返回 Promise 对象 async函数返回一个 Promise 对象. async函数内部return语句返回的 ...

  3. ES6 async函数(超级详细、易懂)

    下面是对 ES6 async函数的整理,希望可以帮助到有需要的小伙伴~ 文章目录 async函数是什么 异步函数声明式 异步函数表达式 返回Promise对象 await表达式 await处理错误 a ...

  4. es6 async函数的实现原理

    async函数的实现原理 async函数的实现原理,就是将 Generator函数和自动执行器,包装在一个函数里. async function fn(args) { // ... } // 等同于 ...

  5. 在C 函数中保存状态:registry、reference和upvalues

    在C函数中保存状态:registry.reference和upvalues      C函数能够通过堆栈来和Lua交换数据,但有时候C函数须要在函数体的作用域之外保存某些Lua数据.那么我们想到全局变 ...

  6. ES6箭头函数中的this指向

    1箭头函数中的this (1)箭头函数中没有this : 这意味着 call() apply() bind() 无法修改箭头函数中的this (2)箭头函数中的this指向 :访问上一个作用域的thi ...

  7. es6 async函数的基本用法

    async函数的基本用法 async函数返回一个 Promise 对象,可以使用then方法添加回调函数.当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句 ...

  8. es6 async函数实例:按顺序完成异步操作

    async函数实例:按顺序完成异步操作 实际开发中,经常遇到一组异步操作,需要按照顺序完成.比如,依次远程读取一组 URL,然后按照读取的顺序输出结果. ES6 Promise 的写法如下. func ...

  9. es6 async函数与其他异步处理方法的比较

    async函数与其他异步处理方法的比较 我们通过一个例子,来看 async函数与 Promise.Generator函数的比较. 假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开 ...

  10. es6箭头函数中this

    普通函数: $scope.$on('$stateChangeSuccess',function(){this.list = this.getList();}); 箭头函数: $scope.$on('$ ...

最新文章

  1. debian---nano转VIM
  2. ListView Viewholder的坑 线性布局的坑
  3. Web前端中级认证php,1+x 证书 Web 前端开发中级理论考试 (试卷 6 )
  4. 节点相似性与链路预测
  5. REST 之 Spring 4 RESTful service
  6. sql判断基数_SQL Server中的基数估计框架版本控制
  7. 又延伸到socket去了。
  8. JavaSE基本语法练习题(下)
  9. linux下安装yum步骤
  10. C语言控制台美化教程
  11. 浏览器可下载的网页链接(百度云链接等),而迅雷(等外置软件)下载失败
  12. C语言 输出螺旋数组
  13. 学习java兴趣之作模仿原神抽卡模块。希望哥、姐指点一下以下是代码;
  14. iptables中DNAT、SNAT和MASQUERADE
  15. Epicor 客制化 - 常用对象
  16. 【JS】js判断等号、等同(===、==)
  17. Java语言每日一练—第10天:谁是胖子
  18. C++函数UpdateData()有什么作用?
  19. 护眼灯色温和显色指数哪个重要?台灯显色和色温的关系是什么
  20. MySQL Packet for query is too large (xxx > xxx)...by setting the ‘max_allowed_packet‘ variable.的解决办法

热门文章

  1. Virtual Box 打开.vmdk文件
  2. 五线谱音名和组别对照表_五线谱最全知识及符号! 太实用了,100%收藏!!!...
  3. c语言趣味菜单实验报告,DSP实验报告+心得体会
  4. SQL排序(升序,降序)
  5. matlab批量修改指定像素
  6. emoji表情包整理好的
  7. H3C华三路由器nat避免生成null 0路由并解决nat需求
  8. try的动词用法_try的用法_tries与try的用法,请分别举例说明。谢!
  9. 动态ip如何做域名解析
  10. [BZOJ4556][Tjoi2016Heoi2016]字符串(二分+后缀数组+主席树)