本文来自:https://ekyu.moe/article/limits-of-native-promise-and-async-await/

众所周知,Nodejs 已原生支持 Promise 和 async/await 关键字,异步控制已经变得更加方便。

然而,如今仍有很多人选择使用第三方的 Promise 库(如 bluebird)和类似 async/await 的实现(如 co)。这并不完全是历史原因使然,而是原生 Promise 和 async/await 仍存在着许多不足之处。本文将简单地提出一些,希望能抛砖引玉。

原生 Promise 方法匮乏

尽管原生 Promise 提供了Promise.allPromise.race这类基本的方法,但在日常使用中,我们经常需要像Promise.mapPromise.reducePromise.promisifyPromise.delayPromise#isPendingPromise#cancelPromise#timeout这样的方法。尽管要手动实现这些也不算难(除了Promise#cancel),但一些第三方的 Promise 库已经实现了部分这类 feature,就暂且没有必要重复造轮子了。

17-05-31 更新
刚刚发现 Nodejs 发布了 8.0.0,Changelog 中有一行吸引了我的注意。

The new util.promisify() API has been added [99da8e8e02#12442.

我查了一下文档,确实加了这个方法!好兴奋!!

原生 Promise 有性能缺陷

之前看到这个问题有提到原生 Promise 的效率问题,blubird 的作者 Petka Antonov 给出了回答,这里提一下回答中的要点。

  1. V8 的 Promise 是用 JavaScript 而不是 C 实现的。即便使用 C 语言实现 Promise 也不能提高多少性能,因为实际上它所做的提挈都是在与 JavaScript 对象交互。
  2. V8 的 Promise 实现没有像 bluebird 一样的优化,比如它会为 Promise 的 handlers 创建数组,当每个Promise都必须要创建一对数组的时候会消耗大量的内存,而实际上在 99.99% 的情况下都不会将一个 Promise 分支多次,因而对这类情况进行优化能减小内存的使用。
  3. 即便 V8 的 Promise 像 bluebird 一样地被优化,它仍然会受到规范的牵制。这个 benchmark 用了 new Promise(在 bluebird 中这被视为一种 anti-pattern),因为在 ES6 中并没有别的办法来生成一个根 Promise。用new Promise来创建一个 Promise 是极慢的。首先父函数会申请一个闭包,然后传入两个独立的闭包作为参数。算起来每生成一个 Promise 总共要申请三个闭包,然而相对于一个被优化的 Promise 而言,一个闭包都已经是个更昂贵的对象了。

兼容性

并不是所有的环境都能随随便便地升级到能支持 Promise 和 async/await 的 node 版本,尤其是 async/await 需要 Nodejs 7.6.0 以上,在我写这篇 blog 的时候 Nodejs 的 LTS 版本还是 6.10.3。而相比之下,很多第三方库都做了这个 polyfill。

async function 返回的是原生 Promise

目前没有办法向 async function 的调用返回值注入别的类型的 Promise,它返回的只能是原生的。而上文提到了原生 Promise 目前存在的很多不足,在不得不使用第三方 Promise 库的情况下,也不得不在很多地方放弃 async function。尽管,将一个原生的 Promise 转为 bluebird 的 Promise 倒也不是不可能,例如:

const Bluebird = require('bluebird');

const someTask = (async () => {})();
const bluebirdSomeTask = Bluebird.resolve(someTask); console.log(someTask instanceof Promise); //=> true
console.log(bluebirdSomeTask instanceof Bluebird); //=> true

然而用 Bluebird.resolve 这种方法来进行转换看上去就非常 anti-pattern,也徒增了代码的复杂度。


本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。
本文链接:https://ekyu.moe/article/limits-of-native-promise-and-async-await/

原生的 promise 的局限性的更多相关文章

  1. 原生Ajax + Promise

    有原生写的ajax + promise嫁接下 ;(function(root){ var LD = function(obj){ if( obj instanceof LD ) return obj; ...

  2. promise应用及原生实现promise模型

    一.先看一个应用场景 发送一个请求获得用户id, 然后根据所获得的用户id去执行另外处理.当然这里我们完全可以使用回调,即在请求成功之后执行callback; 但是如果又添加需求呢?比如获得用户id之 ...

  3. 【es6】js原生的promise

    JavaScript 是单线程的,这意味着任何两句代码都不能同时运行,它们得一个接一个来.在浏览器中,JavaScript 和其他任务共享一个线程,不同的浏览器略有差异,但大体上这些和 JavaScr ...

  4. js原生方法promise的实现

    一会儿就要回家过年了,再来手写一个promise吧,要不等着下班真的煎熬... <!DOCTYPE html> <html lang="en"> <h ...

  5. 浅谈ES6原生Promise

    浅谈ES6原生Promise 转载 作者:samchowgo 链接:https://segmentfault.com/a/1190000006708151 ES6标准出炉之前,一个幽灵,回调的幽灵,游 ...

  6. angular2系列教程(七)Injectable、Promise、Interface、使用服务

    今天我们要讲的ng2的service这个概念,和ng1一样,service通常用于发送http请求,但其实你可以在里面封装任何你想封装的方法,有时候控制器之间的通讯也是依靠service来完成的,让我 ...

  7. ES6 - Note5:Promise

    1.Promise介绍 Promise最早是社区提出和实现,后面ES6将其写入标准,并原生提供Promise对象,是一种异步编程的解决方案,具体的概念大家可以去查看相关的资料.传统上处理异步都是以ca ...

  8. js promise chain

    新的标准里增加了原生的Promise. 这里只讨论链式使用的情况,思考一下其中的细节部分. 一,关于 then() 和 catch() 的复习 then() 和 catch() 的参数里可以放置 ca ...

  9. Promise对象

    1.Promise思想:每一个异步任务立刻返回一个Promise对象,由于是立刻返回,所以可以采用同步操作的流程.这个Promises对象有一个then方法,允许指定回调函数,在异步任务完成后调用. ...

随机推荐

  1. Hibernate中的映射关系(一对多)

    在数据库中表和表之间的关系有几种,(一对一,一对多,多对多)一对一关系:可以选择任意一方插入外键(one-to-one:one-to-one<--->many-to-one[unique= ...

  2. SignalR循序渐进(一)简单的聊天程序

    前阵子把玩了一下SignalR,起初以为只是个real-time的web通讯组件.研究了几天后发现,这玩意简直屌炸天,它完全就是个.net的双向异步通讯框架,用它能做很多不可思议的东西.它基于Owin ...

  3. Exchange OAB(Offline Address Book)

    If Outlook is left running constantly in Cached Exchange Mode, it updates the Offline Address Book a ...

  4. 并发编程 - 协程 - 1.协程概念/2.greenlet模块/3.gevent模块/4.gevent实现并发的套接字通信

    1.协程并发:切+保存状态单线程下实现并发:协程 切+ 保存状态 yield 遇到io切,提高效率 遇到计算切,并没有提高效率 检测单线程下 IO行为 io阻塞 切 相当于骗操作系统 一直处于计算协程 ...

  5. Spring整合JUnit4进行AOP单元测试的时候,报:"C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.3\lib\idea_rt.jar=64

    错误代码 "C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" -ea -Didea.test.cyclic.buffer.size= ...

  6. LInux进程虚拟地址空间的管理

    2017-04-07 脱离物理内存的管理,今天咱们来聊聊进程虚拟内存的管理.因为进程直接分配和使用的都是虚拟内存,而物理内存则是有系统“按需”分配给进程,在进程看来,只知道虚拟内存的存在! 前言: 关 ...

  7. python常见模块之time模块

    一.模块简介 在Python中,通常有这几种方式来表示时间: 时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量.我们运行“type(tim ...

  8. linux定时任务常用命令大全

    脚本中时间戳 TIMESTAMP=`date +%Y%m%d%H%M%S`

  9. socket编程之obj压缩加密传输

    因为需要序列化这个对象以便在网络上传输.所以POJO必需要实现java.io.Serializable接口.使用了 ObjectInputStream和ObjectOutputStream来接收和发送 ...

  10. SpringBoot-模板渲染

    模板 开发Web站点的本质,其实就是根据浏览器发起的请求(输入),生成HTML代码返回给浏览器(输出).在之前的学习中,我们已经通过文件的形式存储起来而不是直接在Java代码中生成HTML代码.另一方 ...