web-dev-qa-db-ja.com

ES6のプロモーションがあるのに、QやBlueBirdのようなプリズムライブラリを使用する理由はまだありますか?

Node.jsがプロミスのネイティブサポートを追加した後も、QやBlueBirdのようなライブラリを使用する理由はまだありますか?

たとえば、新しいプロジェクトを始めていて、このプロジェクトにこれらのライブラリを使用する依存関係がないとしましょう。そのようなライブラリを使用する理由はこれ以上ないと言えますか?

211
Murat Ozgul

古い格言は、あなたが仕事のために正しい道具を選ぶべきであるということです。 ES6の約束が基本を提供します。あなたがこれまでに望んでいた、あるいは必要としていたことが基本だけであれば、それはあなたのためにちょうどうまくいくでしょう。しかし、ツールビンには基本だけでなく、より多くのツールがあり、それらの追加ツールが非常に役立つ状況があります。そして私は、ES6の約束には、ほぼすべてのnode.jsプロジェクトで役立つ約束のような基本事項のいくつかさえ欠けていると主張します。

私は Bluebirdのpromiseライブラリ に最も精通しているので、そのライブラリでの経験から主に話すことにします。

だから、ここで私はより有能なPromiseライブラリを使用する6つの理由があります

  1. Non-Promisified非同期インターフェース - .promisify().promisifyAll()は、まだプレーンコールバックを必要とし、まだpromiseを返さないすべての非同期インターフェースを処理するのに非常に便利です。インタフェース全体のバージョン.

  2. 高速 - Bluebirdは、ほとんどの環境でネイティブの約束よりも はるかに高速です

  3. 非同期配列反復の順序付け - Promise.mapSeries()またはPromise.reduce()を使用すると、各要素に対して非同期操作を呼び出して配列を反復処理できますが、非同期操作は順番に行われます。すべて同時に。これを行うことができるのは、宛先サーバーがそれを必要とするため、またはある結果を次の結果に渡す必要があるためです。

  4. ポリフィル - 古いバージョンのブラウザクライアントでプロミスを使用したい場合は、ポリフィルが必要です。有能なポリフィルも入手できます。 node.jsにはES6の約束があるので、node.jsにポリフィルを入れる必要はありませんが、ブラウザに入れることができます。 node.jsサーバーとクライアントの両方をコーディングしている場合は、両方で同じpromiseライブラリと機能を使用すると便利です(コードの共有、環境間のコンテキストの切り替え、非同期コードの一般的なコーディング手法の使用など)。 。).

  5. その他の便利な機能 - BluebirdにはPromise.map()Promise.some()Promise.any()Promise.filter()Promise.each()そしてPromise.props()があり、それらは時折便利です。これらの操作はES6の約束と追加のコードで実行できますが、Bluebirdにはこれらの操作があらかじめ組み込まれ、テスト済みであるため、操作が簡単でコードが少なくて済みます。

  6. 組み込み警告とフルスタックトレース - Bluebirdには、おそらく間違ったコードやバグである問題について警告する組み込み警告がいくつかあります。例えば、.then()ハンドラーの中でその約束を返さずに新しい約束を作成する関数を呼び出すと(それを現在の約束チェーンにリンクするため)、ほとんどの場合、それは偶然のバグであり、Bluebirdはあなたに警告を与えます。その効果他の組み込みBluebirdの警告は ここで説明されています

これらのさまざまなトピックについて、もう少し詳しく説明します。

PromisifyAll

どのnode.jsプロジェクトでも、fsモジュールのような標準のnode.jsモジュールで.promisifyAll()を多用しているので、私はすぐにどこでもBluebirdを使用します。

Node.js自体は、fsモジュールのように非同期IOを実行する組み込みモジュールへの約束のインターフェースを提供しません。ですから、それらのインターフェースでpromiseを使いたいのであれば、使用する各モジュール関数の周囲にpromiseラッパーをコーディングするか、promiseを使用しない、または使用しないライブラリを入手することになります。

BluebirdのPromise.promisify()Promise.promisifyAll()は、promiseを返すための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()は便利な関数に加えて、同時に実行できる操作の数を指定できる同時実行性オプションもサポートしています。外部リソース.

これらのうちのいくつかは、スタンドアロンと呼ばれ、それ自身が多くのコードを節約することができるイテラブルに解決されるという約束で使われることができます。


ポリフィル

ブラウザプロジェクトでは、一般的にはPromiseをサポートしていないブラウザもサポートしたいので、とにかくポリフィルが必要になります。 jQueryも使っているのであれば、jQueryに組み込まれているpromiseサポートだけを使うこともできます(ただし、jQuery 3.0で修正される可能性があります)。ただし、プロジェクトに大きな非同期アクティビティが含まれる場合は、 Bluebirdの拡張機能はとても便利です。


速い

Bluebirdの約束はV8に組み込まれている約束よりもかなり速いように見えることにも注目に値する。このトピックに関するさらなる議論については この投稿 を見てください。


Big Thing 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
});

ES6のpromiseサポートをnode.jsに組み込んでも、組み込みモジュールのいずれもpromiseを返さないようにするのは少し奇妙に思えます。これはnode.jsで整理する必要があります。それまでは、Bluebirdを使ってライブラリ全体を宣伝しています。それで、組み込みモジュールのどれもが最初に手動でラッピングすることなしにそれらと一緒にpromiseを使用させないので、promiseが現在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
});

Bluebirdの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
});

リモートホストから一度に4つのURLを読むことができますが、できる限り多くの要求を並列に保ちたい場合の、単純なPromisesとBluebirdのpromisifyおよびPromise.map()の例を示します。

プレーンな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
});
349
jfriend00