1. ホーム
  2. ジャバスクリプト

[解決済み】ES6プロミスがある今、QやBlueBirdのようなプロミスライブラリを使う理由はまだあるのでしょうか?[クローズド]。

2022-04-03 21:32:22

質問

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

例えば、新しいプロジェクトを始めるときに、このプロジェクトではこれらのライブラリを使う依存関係がないと仮定した場合、このようなライブラリを使う理由はもうないと言えるのでしょうか?

どのように解決するのか?

古い格言に、仕事には正しい道具を選ぶべきだというものがあります。 ES6は、基本的なものを提供することを約束します。 もし、あなたが必要とするものが基本的なものだけなら、それはあなたにとってうまく機能するはずです。 しかし、道具箱の中には基本的なものだけでなく、もっと多くの道具があり、それらの追加の道具が非常に役に立つ状況もあります。 そして、私は、ES6プロミスが、ほとんどすべてのnode.jsプロジェクトで有用であるプロミス化のような基本的なもののいくつかを欠いているとさえ主張します。

私が最もよく知るのは ブルーバードプロミスライブラリ というわけで、主にこのライブラリに関する経験からお話します。

そこで、より高性能なPromiseライブラリを使用する理由トップ6を紹介します。

  1. 非推奨の非同期インターフェイス - .promisify().promisifyAll() は、まだプレーンなコールバックを必要とし、プロミスを返さない非同期インターフェイスを扱うのに非常に便利です。

  2. <強い より速く - ブルーバードは 格段に速い ほとんどの環境でネイティブプロミスより

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

  4. ポリフィル - 古いバージョンのブラウザクライアントでプロミスを使用したい場合、いずれにせよポリフィルが必要になります。 有能なポリフィルを入手するのがよいでしょう。 node.jsはES6プロミスを持っているので、node.jsではポリフィルは必要ありませんが、ブラウザでは必要かもしれません。 node.jsのサーバーとクライアントの両方をコーディングしている場合、両方で同じプロミスライブラリと機能を持つことは非常に便利です(コードの共有、環境間のコンテキストスイッチ、非同期コードのための共通のコーディングテクニックなどの使用が容易になります)。

  5. その他の便利な機能 - ブルーバードには Promise.map() , Promise.some() , Promise.any() , Promise.filter() , Promise.each()Promise.props() は、どれもたまに便利なものです。 これらの操作はES6プロミスと追加のコードで実行できますが、Bluebirdはこれらの操作をすでにビルドしてテスト済みなので、よりシンプルに、より少ないコードで使用することができます。

  6. 内蔵の警告とフルスタックトレース - Bluebirdには、おそらく間違ったコードやバグであろう問題を警告する警告が多数内蔵されています。 例えば、新しいプロミスを作成する関数を .then() ハンドラで、そのプロミスを返さずに(現在のプロミスチェーンにリンクするために)、ほとんどの場合、それは偶然のバグであり、Bluebirdはその旨の警告を出します。 その他のBluebirdの組み込み警告は以下の通りです。 ここに記載 .

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

プロミスフィーオール

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

Node.jsは、非同期IOを行う組み込みモジュールに対して、プロミスのインターフェイスを提供しません。 fs モジュールです。 そのため,これらのインターフェイスでプロミスを使いたい場合は,使用するモジュール関数の周りにプロミスラッパーをハンドコードするか,それをやってくれるライブラリを入手するか,プロミスを使わないかのどちらかになります.

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

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関数ごとに手作業でこれを行う必要があります。 これでは明らかに意味がない。 これは定型的なコードだ。 この作業を代行してくれるユーティリティを手に入れた方がいい。 ブルーバードの Promise.promisify()Promise.promisifyAll() はそのようなユーティリティです。

その他の便利な機能

以下は、私が特に便利だと思うBluebirdの機能です(以下に、これらがどのようにコードの節約や開発のスピードアップにつながるかのコード例をいくつか挙げています)。

Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()

便利な機能だけでなく Promise.map() これは特に、やりたいことがたくさんあるけれども外部のリソースに負担をかけられない場合に便利です。

これらのいくつかは、スタンドアロンで呼び出すことも、それ自体が反復可能なものに解決するプロミスで使用することもでき、多くのコードを節約することができます。


ポリフィル

ブラウザのプロジェクトでは、一般に Promise をサポートしていないブラウザもサポートしたいので、結局はポリフィルが必要になります。 jQueryを使用している場合、jQueryに組み込まれたプロミスサポートを使用することもできます(ただし、いくつかの点で非標準的であり、おそらく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
});

あるいは、ビルトインモジュールの一部として、すでにプロミスのメソッドを提供すればよいのです。

それまでは、ブルーバードでやっています。

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

fs.readFileAsync('somefile.text').then(function(data) {
   // do something with data here
});

ES6のプロミス・サポートがnode.jsに組み込まれていて、組み込みモジュールのどれもがプロミスを返さないというのは少し奇妙な気がします。 これはnode.jsの中で整理される必要があります。 それまでは、私はBluebirdを使ってライブラリ全体をプロミス化しています。 というのも、ビルトインモジュールのどれもが、最初に手動でラップすることなくプロミスを使用することを許さないからです。


プレーンなプロミスとブルーバードのプロミシファイとの比較例です。 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
});


プレーンなプロミスとブルーバードのプロミスとを比較した例です。 Promise.map() 一度に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();
    });
}

ブルーバード・プロミス

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