One, background
1, Node.js asynchronous control
Before writing the callback vs async.js vs promise vs async / await , I introduced the basic usage promise ES6 and ES7 of async / await the.
To be sure, node.js asynchronous control (asynchronous JavaScript), promise that the future of the mainstream , such as async.js and other non-promise library (async.js based callback) will eventually be eliminated, but based on third-party libraries promise of ( Q, when, WinJS, RSVP.js) will be async / await substituted wording.
2, there have been ES6 Promise + async / await, why use bluebird?
But the promise based on the wording of async / await is not very strong. Here could be considered bluebird
, it is a third-party library of Promise , / await the birth earlier than async, but completely compatible, because they are based on Promises/A+
standard (will explain later) it is.
Many third-party libraries are compatible ES6 promise promise, such as Q.
Two, Promise Advanced
1, Promise Past and Present
(1) the definition of
They describe an object that acts as a proxy for a result that is initially unknown, usually because the computation of its value is not yet complete.
Youdao Translation: They describe an object that acts as the initial results of an unknown agent , usually because the calculation of its value has not been completed.
"Agent" is the word used in very good shape.
(2) History
promise
The term was proposed by Daniel P. Friedman and David Wise in 1976.
Later evolved another name: future
, delay
and deferred
, often used interchangeably.
promise origin and related functional programming paradigm (e.g., logic programming), the aim value (Future) its calculated (promise) isolated , thereby allowing for more flexible calculations.
Scenario:
Parallel computing
Distributed Computing
Writing asynchronous procedures to avoid callback hell
(3) Each language support
Now the mainstream language has support for future / promise.
Java 5 in FutureTask (published in 2004)
The .NET 4.5 async / await
Dart(2014)
Python(2015)
Hack(HHVM)
ECMAScript 7(JavaScript)
Scala
The draft C ++
……
2、Promises/A+
Official: https://promisesaplus.com/
介绍:An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
It can be understood as javascript realized about the promise of standards.
3, expand - Promise jQuery in
(1 Introduction
A new feature from the beginning of the introduction of jQuery 1.5.0 version - deferred
object.
Note: Although Deferred also the realization of a promise, but it is not compatible with Promise / A + .
But it can be converted to a standard of promise, for example:
var jsPromise = Promise.resolve($.ajax('/whatever.json'))
(2) Usage
Because jQuery is now rarely used, and only briefly under deferred usage of it.
1 to ajax operations as an example:
$.ajax()
After the operation is completed, if you are using version 1.5.0 jQuery below, returns XHR object , you can not perform chaining; if it exceeds version 1.5.0, is returned deferred objects can be chaining .
# old
$.ajax({
url: "test.html",
success: function(){
alert("哈哈,成功了!");
},
error:function(){
alert("出错啦!");
}
});
# new
$.ajax("test.html")
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
2, Other
$.when()
Similar promise.all ()deferred.resolve()
、deferred.reject()
类似 Promise.resolve()、Promise.reject()……
Three, bluebird
1 Introduction
English documentation:
http://bluebirdjs.com/docs/api-reference.html
Chinese document:
https://itbilu.com/nodejs/npm/VJHw6ScNb.html
2, installation
npm install bluebird
3, using
const Promise = require('bluebird')
Write overrides Promise native objects.
4, early primary performance issues
Early js standard library does not contain Promise, it can only be forced to use third-party libraries Promise, for example bluebird.
Later ES6 and ES7 have launched a native of Promise and async / await, but poor performance, we also used, for example bluebird.
But the Node.js v8.x, native performance has been greatly optimized to such third party without the use of bluebird Promise library. (Unless you need to use more feature bluebird, while the native is not available. This will be described in detail below)
For more information please refer to the article: the Node 8: meet async await a new era
Four, bluebird usage
This chapter will (mainly ES7 of async / wait) in conjunction with bluebird usage and native investigate the optimal writing.
1, the callback form -> Promise form
Most NodeJS standard library API and many third-party library API uses a model callback method, that is, when performing asynchronous operations, need to pass a callback method to accept the results of operations and errors may occur.
E.g. NodeJS standard library fs
modules:
const fs = require('fs'),
path = require('path');
fs.readFile(path.join(__dirname, 'sample.txt'), 'utf-8', (err, data) => {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
(1)bluebird
For such a method, the Bluebird promisifyAll()
and promisify()
can easily be used to convert them into a form Promise.
// 覆盖了原生的Promise
const Promise = require('bluebird'),
fs = require('fs'),
path = require('path');
// 1、promisifyAll
// Promise.promisifyAll 方法可以为一个对象的属性中的所有方法创建一个对应的使用 Promise 的版本
Promise.promisifyAll(fs);
// 这些新创建方法的名称在已有方法的名称后加上"Async"后缀
// (除了 readFile 对应的 readFileAsync,fs 中的其他方法也都有了对应的 Async 版本,如 writeFileAsync 和 fstatAsync 等)
fs.readFileAsync(path.join(__dirname, 'sample.txt'), 'utf-8')
.then(data => console.log(data))
.catch(err => console.error(err));
// 2、promisify
// Promise.promisify 方法可以为单独的方法创建一个对应的使用 Promise 的版本
let readFileAsync = Promise.promisify(fs.readFile)
readFileAsync(path.join(__dirname, 'sample.txt'), 'utf-8')
.then(data => console.log(data))
.catch(err => console.error(err));
(2) primeval
In node.js 8.x versions, can be used util.promisify()
to achieve promisify () function the same.
Before the official launch of this tool, there are many folk have similar tools, in addition to bluebird.promisify, there are such es6-promisify, thenify.
2, using the promise - .finally ()
.finally()
You can avoid making the same statements require () each time in writing then () and catch the situation.
(1)bluebird
Promise.reject(new TypeError('some error'))
.catch(TypeError, console.error)
.finally(() => console.log('done'));
(2) to achieve their own
Promise.prototype.finally = function (callback) {
return this.then(function (value) {
return Promise.resolve(callback()).then(function () {
return value;
});
}, function (err) {
return Promise.resolve(callback()).then(function () {
throw err;
});
});
};
(3)async / await
With a try ... catch ... finally is finally can be realized.
(4) primeval
.finally () is ES2018 (ES9) new features.
3、使用 promise —— .cancel()
(1)bluebird
When a Promise object is .cancel()
after, but its callback method will not be called, and will not cancel an ongoing asynchronous operation .
// 先修改全局配置,让 promise 可被撤销
Promise.config({
cancellation: true, // 默认为 false
});
// 构造一个 promise 对象,并设置 1000 ms 延迟
let promise = Promise.resolve("hello").then((value) => {
console.log("promise 的 async function 还是执行了……")
return value
}).delay(1000)
// promise 对象上绑定回调函数
promise.then(value => console.log(value))
// 取消这个 promise 对象的回调
setTimeout(() => {
promise.cancel();
}, 500);
输出:
promise 的 async function 还是执行了……
Mentioned here .delay () method will be described below.
(2)async / await
By the return value of the async / await function calls to do if judgment to decide whether or not to execute the next logical.
4, a set of processing promise
Before the code samples for a single Promise. In practice, often deal with relations with more of Promise.
(1)bluebird
To fs module are read sample1.txt
, sample2.txt
, sample3.txt
the contents of three files as an example. Their contents of the file are "1", "2", "3."
const Promise = require('bluebird'),
fs = require('fs'),
path = require('path');
Promise.promisifyAll(fs);
// 一、并行操作
// 1、Promise.all ,必须全部成功才通过 【保证返回顺序】
Promise.all([
fs.readFileAsync(path.join(__dirname, 'sample1.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample2.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample3.txt'), 'utf-8')
]).then(results => console.log(results.join(', '))).catch(console.error);
// 1.1、Promise.props ,约等于 Promise.all,但不同的在于: 返回的不是数组而是对象 !
Promise.props({
app1: fs.readFileAsync(path.join(__dirname, 'sample1.txt'), 'utf-8'),
app2: fs.readFileAsync(path.join(__dirname, 'sample2.txt'), 'utf-8'),
app3: fs.readFileAsync(path.join(__dirname, 'sample3.txt'), 'utf-8'),
}).then(results => console.log(results)).catch(console.error);
// 1.2 Promise.join,约等于 Promise.all 【保证返回顺序】, 但不同的在于: 成功结果不是 array 而是多个参数 !
Promise.join(
fs.readFileAsync(path.join(__dirname, 'sample1.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample2.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample3.txt'), 'utf-8'),
(a, b, c) => console.log(a, b, c));
// 1.3、Promise.filter ,约等于 Promise.all 之后对成功结果的 Array 进行 filter 过滤 【保证返回顺序】
Promise.filter([
fs.readFileAsync(path.join(__dirname, 'sample1.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample2.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample3.txt'), 'utf-8')
], value => value > 1).then(results => console.log(results.join(', '))).catch(console.error);
// ----------
// 2、Promise.map ,约等于 Promise.all 【保证返回顺序】
Promise.map(['sample1.txt', 'sample2.txt', 'sample3.txt'],
name => fs.readFileAsync(path.join(__dirname, name), 'utf-8')
).then(results => console.log(results.join(', '))).catch(console.error);
// 2.1 Promise.reduce,约等于 Promise.map
Promise.reduce(['sample1.txt', 'sample2.txt', 'sample3.txt'],
(total, name) => {
return fs.readFileAsync(path.join(__dirname, name), 'utf-8').then(data => total + parseInt(data));
}
, 0).then(result => console.log(`Total size: ${result}`)).catch(console.error);
// ----------
// 3、Promise.some 只要成功 N 个就通过 【不保证返回顺序】
Promise.some([
fs.readFileAsync(path.join(__dirname, 'sample1.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample2.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample3.txt'), 'utf-8')
], 3).then(results => console.log(results.join(', '))).catch(console.error);
// 3.1、Promise.any 只要成功 1 个就通过,约等于 Promise.some (N = 1),但不同的在于:返回的不是数组而是单个值了!
Promise.any([
fs.readFileAsync(path.join(__dirname, 'sample1.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample2.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample3.txt'), 'utf-8')
]).then(results => console.log(results)).catch(console.error);
// 3.2、Promise.race 只要成功 1 个就通过,约等于 Promise.any (N = 1),但不同的在于:如果成功返回前遇到了失败,则会不通过!
Promise.race([
fs.readFileAsync(path.join(__dirname, 'sample1.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample2.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, 'sample3.txt'), 'utf-8')
]).then(results => console.log(results)).catch(console.error);
// ----------
// 二、串行
// 4、Promise.mapSeries ,约等于 Promise.map 【保证返回顺序】,但不同的在于: 这是串行不是并行!
Promise.mapSeries(['sample1.txt', 'sample2.txt', 'sample3.txt'],
name => fs.readFileAsync(path.join(__dirname, name), 'utf-8').then(function(fileContents) {
return name + "!";
})
).then(results => console.log(results.join(', '))).catch(console.error);
// 'sample1.txt!, sample2.txt!, sample3.txt!'
// 4.1、Promise.each ,约等于 Promise.mapSeries 【保证返回顺序】, 但不同的在于: 只是单纯的遍历,每次循环的 return 毫无影响 !
Promise.each(['sample1.txt', 'sample2.txt', 'sample3.txt'],
name => fs.readFileAsync(path.join(__dirname, name), 'utf-8').then(function(fileContents) {
return name + "!"; // 无效
})
).then(results => console.log(results.join(', '))).catch(console.error);
// 'sample1.txt, sample2.txt, sample3.txt'
1, most functions are parallel in. Where the map, filter as well Concurrency coordination
(concurrent coordination) function.
note:
1, because Node.js is single-threaded, concurrent here just for the promise, the fact underlying or serial .
2, the number of concurrent number, depending on the specific functions performed by your promise, such as network requests, database connections. It needs to be set according to the actual situation.
To map, for example:
// 控制并发数
Promise.map(['sample1.txt', 'sample2.txt', 'sample3.txt'],
name => fs.readFileAsync(path.join(__dirname, name), 'utf-8'),
{concurrency: 2}
).then(results => console.log(results.join(', '))).catch(console.error);
2, mapSeries, each is serial , can be seen as {concurrency: 1}
a special case.
(2) expand - promiseAll implementation principle
function promiseAll(promises) {
return new Promise(function(resolve, reject) {
if (!isArray(promises)) {
return reject(new TypeError('arguments must be an array'));
}
var resolvedCounter = 0;
var promiseNum = promises.length;
var resolvedValues = new Array(promiseNum);
for (var i = 0; i < promiseNum; i++) {
(function(i) {
Promise.resolve(promises[i]).then(function(value) {
resolvedCounter++
resolvedValues[i] = value
if (resolvedCounter == promiseNum) {
return resolve(resolvedValues)
}
}, function(reason) {
return reject(reason)
})
})(i)
}
})
}
Note: Promise.resolve(promises[i])
this means, is prevent promises [i] non-target promise, promise to forcibly turn the object.
This source address is: Promise-All-the Simple
(3)async / await
For the above parallel operation, suggested bluebird (native looks like now only supports Promise.all (), too little).
For the above serial operation, the cycle can be used with async / await can.
5, resource use and release
If a resource to be released in Promise, as the database connection, we need to ensure that these resources should be released.
(1)bluebird
Method 1: finally () to add a resource release code (introduced above)
Method 2 [Recommended]: the use of the resource release (disposer) and Promise.using ().
(2)async / await
Use async await the try / ... catch ... finally in a finally .
6, Timer
(1)bluebird
async function test() {
try {
let readFilePromise = new Promise((resolve, reject) => {resolve('result')})
let result = await readFilePromise.delay(1000).timeout(2000, 'timed out')
console.log(result);
} catch (err) {
console.log("error", err);
}
}
test();
1, the default, new Promise will be executed immediately, but added delay()
, can delay the execution.
2, timeout()
you can set timeout time execution, ie thrown over TimeoutError
error.
(2)async / await
Temporary no convenient alternative wording.
7, practical approach
(1)bluebird
bluebird's Promise also contains some practical methods. tap
And tapCatch
it is used to view the results of Promise and errors. Treatment of these methods does not affect the result of the Promise, adapted to perform logging. call
Promise for the result object method call. get
Promise for getting a property value in the result object. return
To change the outcome of Promise. throw
To throw an error. catchReturn
After capture for error, change the value of the Promise. catchThrow
After capture for error, throws new errors.
(2)async / await
Practical method above bluebird in writing async / await the years, seem insignificant.
8, error handling
(1) expand - then () is specified more than once with the error
For a resolve of promise, specify more then:
let promiseObj = new Promise((resolve, reject) => {resolve()})
// 第一次指定 then
promiseObj.then(function (data) {
console.log("success1");
}, function (data) {
console.log("fail1");
})
// 第二次指定 then
promiseObj.then(function (data) {
console.log("success2");
}, function (data) {
console.log("fail2");
})
// 第三次指定 then
promiseObj.then(function (data) {
console.log("success3");
})
// 第四次指定 then(catch)
promiseObj.catch(function (data) {
console.log("fail4");
})
输出:
success1
success2
success3
On the promise of a reject, specify more then:
let promiseObj = new Promise((resolve, reject) => {reject()})
// 第一次指定 then
promiseObj.then(function (data) {
console.log("success1");
}, function (data) {
console.log("fail1");
})
// 第二次指定 then
promiseObj.then(function (data) {
console.log("success2");
}, function (data) {
console.log("fail2");
})
// 第三次指定 then
promiseObj.then(function (data) {
console.log("success3");
})
// 第四次指定 then(catch)
promiseObj.catch(function (data) {
console.log("fail4");
})
输出:
fail1
fail2
fail4
Unhandled rejection undefined
in conclusion:
1, for a promise object, we can specify many times its then ().
2, when this promise state to resolve, even without then () or there is then () but no successCallback, there will not be a problem.
3, when this promise state to reject, if not then () or there is then () but no failureCallback, it will error (below will explain how to capture this wrong).
(2)bluebird
1, local error handling
Using the then () of failureCallback (or .catch ()). I do not go into details.
2, global error handling
bluebird provides two promise was denied related global event , respectively unhandledRejection
, and rejectionHandled
:
let promiseObj = new Promise((resolve, reject) => {reject('colin')})
setTimeout(() => {
promiseObj.catch(function (data) {
console.log("fail");
})
}, 2000);
process.on('unhandledRejection', (reason, promise) => console.error(`unhandledRejection ${reason}`));
process.on('rejectionHandled', (reason, promise) => console.error(`rejectionHandled ${reason}`));
输出:
unhandledRejection colin
rejectionHandled [object Promise]
fail
1, promise not to reject processing (i.e., above), it will trigger unhandledRejection
event
2, but may be delayed for processing reject to the next event loop was only executed, it will trigger rejectionHandled
event
So much and so we rejectionHandled event, prevent miscarriage of justice, it can be written in the following global error handling code:
let possiblyUnhandledRejections = new Map();
// 当一个拒绝未被处理,将其添加到 map
process.on("unhandledRejection", function(reason, promise) {
possiblyUnhandledRejections.set(promise, reason);
});
process.on("rejectionHandled", function(promise) {
possiblyUnhandledRejections.delete(promise);
});
setInterval(function() {
possiblyUnhandledRejections.forEach(function(reason, promise) {
// 做点事来处理这些拒绝
handleRejection(promise, reason);
});
possiblyUnhandledRejections.clear();
}, 60000);
(3) async / await error handling
async / await the try..catch does not fully capture all of the errors.
1, local error handling
With a try ... catch can.
Note: missing error conditions:
run () The promise itself reject the
async function run() {
try {
// 注意这里没有 await
return Promise.reject();
} catch (error) {
console.log("error",error)
// 代码不会执行到这里
}
}
run().catch((error) => {
// 可以捕获
console.log("error2", error)
});
Solution: for the run () function (top-level function) do catch capture.
2, global error handling
Missing error conditions:
But was not present promise reject processed () this internal promise run
async function run() {
try {
// 注意这里 即没有 await 也没有 return
Promise.reject();
} catch (error) {
console.log("error", error)
// 代码不会执行到这里
}
}
run().catch((error) => {
// 不可以捕获
console.log("error2", error)
});
Solution:
1, with bluebird global error handling as described above, with good unhandledRejection
and rejectionHandled
global events.
2, ES6 also support native unhandledRejection
and rejectionHandled
global events.