js中的promise和async|await的区别?

关注者
145
被浏览
42,653

17 个回答

先说结论

  • Promise最早是没有的,ajax出现之后异步开始多了,2009年node出现,callback hell求解过程中才有了Promise a、b、c、d等规范,最终确定的是promise a+规范,后面浏览器里加了Promise对象。很长一段时间native promise的性能很差,都要用bluebird这种库替代,大约2018年之后,node 8以后,native promise已经比bluebird更好。于是一代神器bluebird陨灭了。
  • Async函数/Await(以前说是ES7 stage-3,现在已成规范),其v8中实现是generator的代码上加的语法糖。

相同点

  • 解决异步流程问题,promise是约定,而async更优雅。

区别

  • Promise是es6里的,async是es7了
  • Promise原来有规范的意义,Promise a、b、c、d等规范,最终确定的是promise a+规范
  • Promise链式操作,自己catch异常。async则要在函数内catch,好在现在catch成本较低。
  • Promise有很多并行神器,比如Promise.all\Promise.race等。这些是async没法搞定的。
  • Promise是显式的异步,而Async/await 让你的代码看起来是同步的,你依然需要注意异步。
  • Promise即使不支持es6,你依然可以用promise的库或polyfil,而async就很难做,当然也不是不能,成本会高很多。
  • async functions 和 Array.forEach等结合,很多tc39提案都在路上或者已经实现,处于上升期,而promise也就那样了。

参见

i5ting/asynchronous-flow-controlgithub.com图标

js流程控制的演进过程,分以下6部分:

  1. 同步代码
  2. 异步JavaScript: callback hell
  3. Thunk
  4. Promise/a+
  5. 生成器Generators/yield
  6. Async函数/Await(以前说是ES7 stage-3)

看起来挺简单的,作为*js(沾边)工程师的各位自测一下,当前是哪个阶段?

generator是es6的一个特性,本身是用于计算的,通过generator和yield写的代码看起来像同步的,主要是yield来处理同步的事儿,但yield又只能在generator里。

在ava里是generator特别简单

4.js

import test from 'ava';

var exec = require('co-exec');

test('generatorFn with exec()', function * (t) {
    let commit = yield exec('ls -alt|grep .gitignore|wc -l');
    t.true(commit == 1);
});

es6的generator本意是为了计算而设计的迭代器,但tj觉得它可以用于流程控制,于是就有了co,co的历史可以说经历了目前所有的流程控制方案,而且由于支持generator和yield就导致yieldable。

实际上co和generator是把双刃剑,给了我们强大便利的同时,也增加了非常多的概念,可能是过渡性的,也可能是过时的。

可是,你真的需要了解这么多么?从学习的角度,当然是多多意义,如果从实用的角度看,你可能不需要。

存在即合理,那么我们就看看这“血案”吧:

  • 学习ES6的Generator
  • 了解ES6的迭代器和迭代器相关的2种协议,了解for-of
  • 了解co和co的2种用法,源码
  • 了解yieldable 5种(包括不常用Thunk)
  • 如果是koa,还需要了解convert和compose

generator执行的时候,需要先生成对象,然后next进行下一步。这样做起来还是比较麻烦,能不能不需要执行器啊?于是async函数就应运而生了。

async函数es7 stage-3的特性,可惜差一点就进入到es7规范了。async函数里使用await可以做到和yield类似的效果,但await只能接promise对象和async函数。

5.js

import test from 'ava';

test('async function', async t => {
    const bar = Promise.resolve('bar');

    t.is(await bar, 'bar');
});

综上所述,学习重点

  • Async函数是趋势,如果Chrome 52. v8 5.1已经支持Async函数(https://github.com/nodejs/CTC/issues/7)了,Node.js支持还会远么?
  • Async和Generator函数里都支持promise,所以promise是必须会的。
  • Generator和yield异常强大,不过不会成为主流,所以学会基本用法和promise就好了,没必要所有的都必须会。
  • co作为Generator执行器是不错的,它更好的是当做Promise 包装器,通过Generator支持yieldable,最后返回Promise,是不是有点无耻?

我整理了一张图,更直观一些。



  • 红色代表Promise,是使用最多的,无论async还是generator都可用
  • 蓝色是Generator,过度货
  • 绿色是Async函数,趋势

结论Promise是必须会的,那你为什么不顺势而为呢?

推荐:使用Async函数 + Promise组合,如下图所示。

好多年前写的,上面的图在狼书卷一里有。曾经也被某大佬的书盗取过。销量很高的书。。。

继续浏览内容
知乎
发现更大的世界
打开
浏览器
继续

Promise是将回调的写法标准化了。实际上早期的js并没有Promise对象,是社区Promise规范出来后,才被吸收到了ECMAScript中。

Promise的问题

Promise没能解决以同步方式写异步代码的问题,嵌套依然存在,仍然会面临大量的then回调。

Async和Await

为了解决上述Promise的问题,await横空出世。然而await是新语法,引擎改动太大,因此要求使用了await的函数前面,必须增加async关键字。

然而V8引擎继续改进,在Chrome 88+中,await提升到了top level,意味着可以丢弃async了。

const a = await Promise.resolve(1)
const b = await Promise.resolve(2)
const c = await Promise.resolve(3)

console.log(a,b,c)

继续浏览内容
知乎
发现更大的世界
打开
浏览器
继续