在Node.js添加对Promise的本机支持之后,还有理由使用Q或BlueBird这样的库吗?
例如,如果您正在启动一个新项目,并假设在该项目中您没有使用这些库的任何依赖项,我们可以说实际上没有更多理由使用这些库了吗?
古老的格言是,您应该选择适合该工作的工具。ES6承诺提供了基础知识。如果您只想要或需要的只是基础知识,那么对您来说应该/可以使用。但是,工具箱中不仅有基础知识,还有更多工具,并且在某些情况下,这些附加工具非常有用。而且,我认为ES6
Promise甚至缺少在几乎每个node.js项目中都有用的诸如Promisification之类的一些基础知识。
我对Bluebird Promise库最熟悉,因此我主要根据我在该库中的经验进行发言。
因此,这是我使用功能更强大的Promise库的6大理由
非Promisified异步接口 - .promisify()
并且.promisifyAll()
是非常有用的,以处理所有仍然需要简单的回调和尚未返回承诺的异步接口-一行代码创建整个界面的promisified版本。
更快 - 在大多数环境中,Bluebird的速度远远快于本机承诺。
异步数组迭代的排序 - Promise.mapSeries()
或Promise.reduce()
允许您遍历数组,在每个元素上调用异步操作,但是对异步操作进行排序,以便它们一个接一个地发生,而不是同时发生。您可以这样做是因为目标服务器需要它,或者因为您需要将一个结果传递给下一个。
Polyfill- 如果您想在较旧版本的浏览器客户端中使用Promise,则仍然需要使用polyfill。也可以得到一个功能强大的polyfill。由于node.js具有ES6 Promise,因此您不需要在node.js中进行polyfill,但可以在浏览器中进行。如果您要同时对node.js服务器和客户端进行编码,则在两者中具有相同的Promise库和功能可能会非常有用(易于共享代码,在环境之间进行上下文切换,对异步代码使用通用的编码技术等)。 )。
其它实用功能 -蓝鸟有Promise.map()
,Promise.some()
,Promise.any()
,Promise.filter()
,Promise.each()
和Promise.props()
所有这一切都是偶然得心应手。尽管可以使用ES6 Promise和其他代码来执行这些操作,但是Bluebird附带了已经预先构建和预先测试的这些操作,因此使用它们的代码更简单,代码更少。
内置警告和完整堆栈跟踪 -Bluebird具有许多内置警告,可警告您可能是错误代码或错误的问题。例如,如果您调用一个在.then()
处理程序中创建新诺言而不返回该诺言(将其链接到当前诺言链)的函数,则在大多数情况下,这是一个偶然的错误,Bluebird会向您发出警告影响。其他内置的Bluebird警告在此处介绍。
以下是有关这些各个主题的更多详细信息:
承诺全部
在任何node.js项目中,我都会在各处立即使用Bluebird,因为我.promisifyAll()
在标准的node.js模块(例如fs
模块)上使用了很多。
Node.js本身并不提供对执行异步IO的内置模块(如该fs
模块)的promise接口。因此,如果要在这些接口上使用promise,则可以手动编写所使用的每个模块html" target="_blank">函数的promise包装器代码,或者获得一个可以为您完成或不使用promise的库。
蓝鸟的Promise.promisify()
和Promise.promisifyAll()
提供的node.js调用约定异步API来回报承诺的自动包装。这非常有用且省时。我用它所有的时间。
这是一个如何工作的示例:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
另一种方法是手动为每个fs
要使用的API 创建自己的Promise包装器:
const fs = require('fs');
function readFileAsync(file, options) {
return new Promise(function(resolve, reject) {
fs.readFile(file, options, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
并且,您必须为要使用的每个API函数手动执行此操作。这显然没有意义。这是样板代码。您也可能会得到一个可以为您完成此工作的实用程序。蓝鸟(Bluebird)Promise.promisify()
和Promise.promisifyAll()
是这样的实用程序。
其他有用的功能
以下是一些我特别认为有用的Bluebird功能(下面有几个代码示例,说明这些功能如何节省代码或加快开发速度):
Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()
除了有用的功能外,Promise.map()
还支持并发选项,该选项可让您指定应允许同时运行多少个操作,这在您有很多事情要做但不能压倒其他事情时特别有用资源。
其中一些既可以称为独立的,也可以用于承诺本身会解析为可迭代的代码,从而可以节省大量代码。
Polyfill
在浏览器项目中,由于您通常仍希望支持某些不支持Promise的浏览器,因此最终还是需要使用polyfill。如果您还使用jQuery,则有时可以只使用内置于jQuery的Promise支持(尽管在某些方面很痛苦,它是非标准的,也许在jQuery
3.0中已修复),但是如果项目涉及任何重大的异步活动,我会发现Bluebird中的扩展功能非常有用。
快点
同样值得注意的是,Bluebird的承诺似乎比V8中内置的承诺要快得多。有关此主题的更多讨论,请参见这篇文章。
Node.js缺少了什么
让我考虑在node.js开发中减少使用Bluebird的原因是,如果node.js内置了promisify函数,那么您可以执行以下操作:
const fs = requirep('fs');
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
或者只是提供已经承诺的方法作为内置模块的一部分。
在此之前,我使用Bluebird进行此操作:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
将ES6Promise支持内置到node.js中并且没有任何内置模块返回Promise似乎有点奇怪。这需要在node.js中解决。在此之前,我使用Bluebird来使整个库更加合理。因此,感觉诺言现在已经在node.js中实现了大约20%,因为没有内置模块允许您将诺言与它们一起使用,而无需先手动包装它们。
这是一个简单的Promises与Bluebird的Promisify的示例,Promise.map()
用于并行读取一组文件并在处理完所有数据时通知:
简单的承诺
const files = ["file1.txt", "fileA.txt", "fileB.txt"];
const fs = require('fs');
// make promise version of fs.readFile()
function fsReadFileP(file, options) {
return new Promise(function(resolve, reject) {
fs.readFile(file, options, function(err, data) {
if (err) return reject(err);
resolve(data);
});
});
}
Promise.all(files.map(fsReadFileP)).then(function(results) {
// files data in results Array
}, function(err) {
// error here
});
蓝鸟Promise.map()
和Promise.promisifyAll()
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const files = ["file1.txt", "fileA.txt", "fileB.txt"];
Promise.map(files, fs.readFileAsync).then(function(results) {
// files data in results Array
}, function(err) {
// error here
});
这是一个简单的Promises与Bluebird的Promisify的示例,并且Promise.map()
当从远程主机读取一堆URL时,您一次最多只能读取4个URL,但希望保持尽可能多的并行请求:
普通JS承诺
const request = require('request');
const urls = [url1, url2, url3, url4, url5, ....];
// make promisified version of request.get()
function requestGetP(url) {
return new Promise(function(resolve, reject) {
request.get(url, function(err, data) {
if (err) return reject(err);
resolve(data);
});
});
}
function getURLs(urlArray, concurrentLimit) {
var numInFlight = 0;
var index = 0;
var results = new Array(urlArray.length);
return new Promise(function(resolve, reject) {
function next() {
// load more until concurrentLimit is reached or until we got to the last one
while (numInFlight < concurrentLimit && index < urlArray.length) {
(function(i) {
requestGetP(urlArray[index++]).then(function(data) {
--numInFlight;
results[i] = data;
next();
}, function(err) {
reject(err);
});
++numInFlight;
})(index);
}
// since we always call next() upon completion of a request, we can test here
// to see if there was nothing left to do or finish
if (numInFlight === 0 && index === urlArray.length) {
resolve(results);
}
}
next();
});
}
bluebird
const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'));
const urls = [url1, url2, url3, url4, url5, ....];
Promise.map(urls, request.getAsync, {concurrency: 4}).then(function(results) {
// urls fetched in order in results Array
}, function(err) {
// error here
});
问题内容: 我不明白…是我还是这是节点中的错误? 可以按预期进行: 这发出了警告: 我懂了 问题答案: 使用与承诺回报新希望(这就是所谓的链接)。因此,当您执行以下操作时: 您的最初承诺在哪里,您将在第1行上创建一个新的承诺(现在不再存在。我们称它为 )。因此,即使您使用with ,也不会处理上的拒绝,这解释了您在控制台上看到的消息。 为了避免出现此消息,您应该在第1行的新承诺中添加a
问题内容: 我无法通过所有论点。我的诺言回调仅收到一个,而不是三个: 知道我在做什么错吗? 问题答案: Q的Promise 只能有一个参数- Promise代表一个单一的值,而不是它们的集合。如果需要多个值,则将它们显式放入数组中。对于多参数回调,您可以使用。
问题内容: 这是一些基于行为的简单问题,我在下面的示例中在节点上运行时注意到了这些行为: 输出为: 1)为什么要实现在立即对已知值运行回调之前等待?为什么不够智能,以至于第一行在第二行运行之前同步发布其输出? 2)什么是之间的时间流逝和被输出?它是单个进程滴答吗? 3)能否将绩效深深包裹在承诺中而产生绩效问题?例如,即使可以有效地同步解决,它是否也要异步等待3倍的时间才能完成? 问题答案: 这实际
问题内容: 关于这两个重要来源:NZakas- 承诺链中的归还承诺 和MDN承诺,我想提出以下问题: 每次我们从承诺履行处理程序返回值时,该值如何传递给从同一处理程序返回的新承诺? 例如, 在这个例子中,是一个承诺。也是来自履行处理程序的承诺。但是。取而代之的是神奇地解决(如何?),然后将该值传递给的实现处理程序。即使是这里的句子也令人困惑。 您能给我解释一下这到底是怎么回事吗?我对这个概念感到困
问题内容: 我想做一些非常简单的事情,但是我一点都不懂… 我希望依次对funcs数组的每个元素进行“处理”,然后输出如下所示: 我无法做到这一点:(应该很简单,我什么都没发现:( 问题答案: 从您的示例来看,我认为您已经看到了Q自述文件中的Sequences部分,但未能理解它。 当每个函数的输出作为输入传递给下一个函数时,原始示例使用“瀑布”模型: 但是您只想按顺序执行我们所有的函数,因此您只需将
问题内容: 无论我的Promise是否成功解决,我都想执行相同的操作。我不想将相同的功能绑定到两个参数。没有像jQuery一样的东西吗?如果没有,我该如何实现? 问题答案: 没有像jQuery一样的东西吗? 如果没有,我该如何实现? 您可以这样实现自己的方法: 或更广泛地讲,将解析信息传递给回调: 两者都确保原始解析得以维持(当回调中没有异常时),并确保等待诺言。