高度な約束 - 非同期/のawait組み合わせブルーバード

一、背景


1、Node.jsの非同期制御

書き込む前に非同期/ VS約束対async.js対コールバックをのawait 、私は基本的な使用法の約束ES6と非同期のES7を導入/待っています。

確かに、(非同期JavaScriptの)非同期制御をNode.jsの、主流の将来のことを約束なasync.jsやその他の非約束ライブラリ(async.jsベースのコールバック)としては、最終的に排除しますが、サードパーティのライブラリに基づいていますが(の約束しますQ、とき、WinJS、RSVP.js)非同期/のawaitの置換された文言になります。

さらに読書:ほとんど知っているが-非同期制御をnodejs「共、非同期、Q、」ES6オリジナルの約束「then.js、青い鳥」の長所と短所は何ですか?どちらの好き?これは、単純な?

2、ES6約束+非同期/のawaitがあった、なぜ使用は青い鳥?

しかし、非同期/のawaitの文言に基づく約束は非常に強力ではありません。ここで考えることができbluebird、それは約束のサードパーティのライブラリは、彼らがに基づいているため、/、以前の非同期よりも出産を待つが、完全な互換性Promises/A+標準(後で説明します)、それはあります。

多くのサードパーティのライブラリは、Qとして互換ES6の約束の約束、です

二、約束高度な


1、約束過去と現在

(1)の定義

彼らは、その値の計算がまだ完了していない通常ので、最初は不明である結果のためのプロキシとして動作するオブジェクトを記述します。

Youdao翻訳:彼らは、未知の初期の結果として動作するオブジェクト記述エージェントを、その値の計算が完了していない通常ので、。

「エージェントは、」非常に良い形で使用される言葉です。

(2)歴史

promise この用語は、1976年にダニエル・P・フリードマンとデビッド・ワイズによって提案されました。

その後、別の名前を進化:futuredelayおよびdeferred、しばしば互換的に使用。

約束起源と関連する関数型プログラミング・パラダイム(例えば、論理プログラミング)、目標値(未来)の算出(約束)を単離し、それによって、より柔軟な計算を可能にします。

シナリオ:

  • 並列コンピューティング

  • 分散コンピューティング

  • コールバック地獄を避けるために、非同期プロシージャを作成

(3)各言語のサポート

今主流の言語は、将来/約束をサポートしています。

  • FutureTaskでJava 5(2004年発行)

  • .NET 4.5非同期/のawait

  • ダート(2014)

  • パイソン(2015)

  • ハック(HHVM)

  • ECMAScriptの7(JavaScriptの)

  • スカラ

  • ドラフトC ++

  • ......

2、約束/ A +

公式:https://promisesaplus.com/

介绍:音のオープンスタンダード、相互運用可能なJavaScriptの約束-によって実装、実装のために。

これは、標準の約束について、実現のjavascriptとして理解することができます。

3、拡大 - 約束のjQueryをして

(1)はじめに

jQueryの1.5.0バージョンの導入の当初から新機能- deferredオブジェクト。

注:がも約束の実現を延期、それは約束/ A +と互換性がありません

しかし、それは例えば、約束の標準に変換することができます。

var jsPromise = Promise.resolve($.ajax('/whatever.json'))
(2)使用

jQueryのは、今、ほとんど使用されない、そしてそれの繰り延べ利用状況の下では簡単にされているので。


一例として、AJAX操作に1:

$.ajax()操作が完了した後は、バージョン1.5.0を使用している場合は、以下のjQueryは、返品のXHRオブジェクトは、あなたが連鎖を実行することはできません。それはバージョン1.5.0を超えた場合、返される繰延オブジェクトが連鎖することができ。

# old
$.ajax({

    url: "test.html",

    success: function(){
      alert("哈哈,成功了!");
    },

    error:function(){
      alert("出错啦!");
    }

  });

# new 
$.ajax("test.html")

  .done(function(){ alert("哈哈,成功了!"); })

  .fail(function(){ alert("出错啦!"); });


2、その他

  • $.when() 同様のpromise.all()

  • deferred.resolve()deferred.reject()类似Promise.resolve()、Promise.reject()

  • ......

三、青い鳥


1.はじめに

英語のドキュメント:

http://bluebirdjs.com/docs/api-reference.html

中国のドキュメント:

https://itbilu.com/nodejs/npm/VJHw6ScNb.html

2、インストール

npm install bluebird

使用して3、

const Promise = require('bluebird')

書き込みオーバーライドは、ネイティブオブジェクトを約束します。

4、初期の主要なパフォーマンスの問題

初期のjsの標準ライブラリには約束が含まれていない、単なる例の青い鳥のために、約束のサードパーティのライブラリを使用するように強制することができます。

その後ES6とES7プロミスと非同期/のawaitのネイティブを立ち上げましたが、パフォーマンスの低下は、我々はまた、例の青い鳥のために、使用されます。

しかし、Node.jsののv8.xは、ネイティブのパフォーマンスが大幅に青い鳥約束ライブラリを使用せずに、このような第三者に最適化されています。(ネイティブが利用できない間、あなたは、より多くの機能の青い鳥を使用する必要がない場合。これは、以下に詳細に説明します)

詳細については、の記事を参照してください。ノード8:素敵な非同期は、新しい時代を待ちます

四、青い鳥の使用


この章では、意志(非同期/待機の主ES7)ブルーバードの使用と一緒にし、ネイティブ最適な書き込みを調査します。

図1に示すように、コールバックのフォーム - >プロミス形

ほとんどのNodeJS標準ライブラリのAPIと多くのサードパーティ製ライブラリのAPIは、非同期操作を実行するとき、操作やエラーの結果を受け入れるようにコールバックメソッドを渡す必要が発生する可能性があり、あるモデルのコールバックメソッドを使用します。

例えば標準ライブラリNodeJS fsモジュール:

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)ブルーバード

例えば、A法、ブルーバード用promisifyAll()及びpromisify()容易形態プロミスに変換するために使用することができます。

// 覆盖了原生的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)原生

Node.jsの8.xのバージョンでは、使用することができるutil.promisify()()と同じ機能promisify達成します。

このツールの公式発表の前に、多くの民族がbluebird.promisifyに加えて、同様のツールを持っているがあり、thenify、このようES6-promisifyがあります。

2、約束を用い - .finally()

.finally()あなたはできるその後、書面で)(同じ文が必要とするたびにすることを避ける()とキャッチ状況を。

(1)ブルーバード
Promise.reject(new TypeError('some error'))
  .catch(TypeError, console.error)
  .finally(() => console.log('done'));
(2)自分自身を達成するために
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)非同期/のawait

ください... catchで...最終的には最終的に実現することが可能とされます。

(4)原生

.finally()ES2018(ES9)の新機能があります。

3、使用約束 - .cancel()

(1)ブルーバード

プロミスオブジェクトがある場合には.cancel()後が、そのコールバックメソッドが呼び出されることはありません、そして継続的な非同期操作をキャンセルしません

// 先修改全局配置,让 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 还是执行了……

この方法は、以下に説明する().delayここで言及。

(2)非同期/のawait

判決は次の論理を実行するか否かを決定する場合は、非同期/のawait関数呼び出しの戻り値によって行います。

図4に示すように、処理約束の組

単一の約束のためのコードサンプルの前に。実際には、多くの場合、約束のよりとの関係を扱います。

(1)ブルーバード

FSモジュールに読み出されsample1.txtsample2.txtsample3.txt3つのファイルの内容は一例として。ファイルのその内容は「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、最も機能は平行です。どこだけでなく、マップ、フィルタConcurrency coordination(同時調整)機能。

注意:

1、Node.jsのはちょうど約束、実際にはここをシングルスレッド、同時であるため、根本的またはシリアル

2は、同時多数の特定の機能に応じて数が、そのようなネットワーク要求、データベース接続など、あなたの約束によって行われます。これは、実際の状況に応じて設定する必要があります。

例えば、マップするには:

// 控制并发数
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、シリアルは、と見ることができる{concurrency: 1}特別なケース。

(2)拡大 - promiseAll実装原理を
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)
    }
  })
}

注:Promise.resolve(promises[i])この手段は、約束[i]は非対象約束、強制的にオブジェクトをオンにする約束を防ぐことです。

このソースアドレスは次のとおりです。約束-全シンプル

(3)非同期/のawait

(今は少なすぎる、Promise.allを()サポートのようなネイティブルックス)以上の並列動作のために、青い鳥を示唆しました。

上記シリアル動作のために、サイクルは、非同期/待機することができるとともに使用することができます。

5、資源利用とリリース

リソースが約束にリリースされる場合は、データベース接続として、私たちは、これらのリソースを解放する必要があることを確認する必要があります。

(1)ブルーバード

方法1:最後に()は、(上で紹介した)リソース解放コードを追加します

方法2 [推奨]:リソース解放(ディスポーザ)の使用とPromise.using()。

(2)非同期/のawait

使用非同期のawaitのtry / ...キャッチ...ついにで最後に

6、タイマー

(1)ブルーバード
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、デフォルト、新しい約束はすぐに実行されますが、追加されdelay()、実行を遅らせることができます。

2、timeout()あなたはすなわち上で投げタイムアウト時間の実行、設定することができますTimeoutErrorエラーを。

(2)非同期/のawait

一時的な無便利な代替表現。

図7に示すように、実用的なアプローチ

(1)ブルーバード

青い鳥の約束にもいくつかの実用的な方法が含まれています。tapそして、tapCatch約束やエラーの結果を表示するために使用されます。約束の結果には影響しません、これらの方法の治療は、ロギングを実行するようになって。call結果オブジェクトのメソッド呼び出しのための約束。get結果オブジェクトのプロパティ値を取得するための約束。return約束の結果を変更するには。throwエラーをスローします。catchReturnエラーのためにキャプチャーした後、約束の値を変更します。catchThrowエラーのためにキャプチャーした後、新たなエラーがスローされます。

(2)非同期/のawait

非同期の書き込みで青い鳥上記の実用的な方法は、/年待って、取るに足りないようです。

8、エラー処理

(1)展開 - 次に()がエラーで複数回指定されています

約束の解決のために、もっとして指定します。

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

拒否の約束を、もっとして指定します。

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

結論:

1、約束のオブジェクトのために、私たちはその後、多くの時間を指定することができます()。

図2に示すように、この約束の状態解決するには、その後もなし()または(がそこである)が、無successCallback、問題ありません。

3、この約束の状態は拒否し、そうでない場合はその後、()または(が続いている)が、無failureCallback、それはエラーになります(以下、この間違ったをキャプチャする方法を説明します)。

(2)ブルーバード

1、ローカルのエラー処理

次いで、使用()failureCallbackの(または.catch())。私は詳細には触れません。


2、グローバルなエラー処理

青い鳥2つの約束が拒否された提供し、関連するグローバルイベント、それぞれunhandledRejection、と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(すなわち、上記)処理拒否しないことを約束し、それがトリガするunhandledRejectionイベントを

2が、処理のために遅延させることができるのみで実行された次のイベントループに拒否、それがトリガするrejectionHandledイベントを

そんなにと正義の流産を防ぐため、イベントをrejectionHandled我々はので、それは次のように記述することができ取り扱うグローバルエラーコードを:

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)非同期/のawaitエラーハンドリング

非同期/完全にすべてのエラーを捕捉しないのtry..catch待っています。


1、ローカルのエラー処理

試してみると...キャッチすることができます。

注:行方不明のエラー条件:

()自体は拒否約束を実行します

async function run() {
    try {
        // 注意这里没有 await
        return Promise.reject();
    } catch (error) {
        console.log("error",error)
        // 代码不会执行到这里
    }
}
run().catch((error) => {
    // 可以捕获
    console.log("error2", error)
});

解決策:実行()関数(トップレベルの機能)のためのキャッチキャプチャを行います。


2、グローバルなエラー処理

エラー条件がありません。

しかし、この内部の約束の実行は)約束は(加工拒否存在しませんでした

async function run() {
    try {
        // 注意这里 即没有 await 也没有 return
        Promise.reject();
    } catch (error) {
        console.log("error", error)
        // 代码不会执行到这里
    }
}
run().catch((error) => {
    // 不可以捕获
    console.log("error2", error)
});

ソリューション:

図1に示すように、ブルーバードグローバルエラー処理と良好で、上記のようにunhandledRejection、およびrejectionHandledグローバルイベント。

2、ES6もネイティブサポートunhandledRejectionし、rejectionHandledグローバルなイベントを。


参考資料

使用して、より強力な約束を達成ブルーバード

おすすめ

転載: www.cnblogs.com/xjnotxj/p/12041074.html