Advanced promise - async / await combination bluebird

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.

Further reading: know almost - nodejs asynchronous control "co, async, Q," es6 original promise ", then.js, bluebird" What are the advantages and disadvantages? Which favorite? Which simple?

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, delayand 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 - deferredobject.

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 fsmodules:

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.txtthe 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 TimeoutErrorerror.

(2)async / await

Temporary no convenient alternative wording.

7, practical approach

(1)bluebird

bluebird's Promise also contains some practical methods. tapAnd tapCatchit 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. callPromise for the result object method call. getPromise for getting a property value in the result object. returnTo change the outcome of Promise. throwTo throw an error. catchReturnAfter capture for error, change the value of the Promise. catchThrowAfter 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 unhandledRejectionevent

2, but may be delayed for processing reject to the next event loop was only executed, it will trigger rejectionHandledevent

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 unhandledRejectionand rejectionHandledglobal events.

2, ES6 also support native unhandledRejectionand rejectionHandledglobal events.


Reference material

Use bluebird achieve more powerful Promise

Guess you like

Origin www.cnblogs.com/xjnotxj/p/12041074.html