1. ホーム
  2. javascript

[解決済み] 既存のコールバックAPIをプロミスに変換するにはどうすればよいですか?

2022-03-24 17:26:57

質問

プロミスを使いたいのですが、コールバックAPIは以下のような形式で用意されています。

1. DOMロードまたはその他の1回限りのイベント。

window.onload; // set to callback
...
window.onload = function() {

};

2. プレーンコールバック

function request(onChangeHandler) {
    ...
}
request(function() {
    // change happened
    ...
});

3. ノードスタイルコールバック("nodeback")。

function getStuff(dat, callback) {
    ...
}
getStuff("dataParam", function(err, data) {
    ...
})

4. ノードスタイルコールバックによるライブラリ全体。

API;
API.one(function(err, data) {
    API.two(function(err, data2) {
        API.three(function(err, data3) {
            ...
        });
    });
});

プロミスでAPIを操作するには、どのように"promisify"すればよいのでしょうか?

解決方法を教えてください。

プロミスには状態があり、保留状態から始まり、解決することができます。

  • 達成済み 計算が正常に終了したことを意味します。
  • 却下 計算が失敗したことを意味します。

プロミスリターン関数 を投げてはいけません。 代わりに拒否を返さなければなりません。プロミスを返す関数から投げる場合、その関数の中で } catch { a .catch . プロミス化されたAPIを使っている人は、プロミスが投げることを期待していない。もしあなたがJSで非同期APIがどのように動作するかわからない場合 - お願いします。 この回答を見る を最初に選択します。

1. DOMロードなどの1回限りのイベント。

つまり、データが利用可能であることを示すために、いつfulfilledまたはrejectedフェーズに移行するかということです。 .then ).

をサポートする最新のプロミス実装では Promise コンストラクタは、ネイティブのES6プロミスと同じです。

function load() {
    return new Promise(function(resolve, reject) {
        window.onload = resolve;
    });
}

そして、出来上がったプロミスを次のように使うことになる。

load().then(function() {
    // Do things after onload
});

deferredをサポートするライブラリで(ここでは例として$qを使用しますが、後でjQueryも使用します)。

function load() {
    var d = $q.defer();
    window.onload = function() { d.resolve(); };
    return d.promise;
}

あるいはjQueryのようなAPIで、一度だけ起こるイベントにフックする。

function done() {
    var d = $.Deferred();
    $("#myObject").once("click",function() {
        d.resolve();
    });
    return d.promise();
}

2. プレーンコールバック

これらのAPIは、JSではコールバックが一般的なので、わりとよく使われています。では、よくあるケースとして onSuccessonFail :

function getUserData(userId, onLoad, onFail) { …

をサポートする最近のプロミス実装では Promise コンストラクタは、ネイティブのES6プロミスと同じです。

function getUserDataAsync(userId) {
    return new Promise(function(resolve, reject) {
        getUserData(userId, resolve, reject);
    });
}

deferredをサポートするライブラリで(ここではこの例でjQueryを使ってみますが、上記では$qも使っています)。

function getUserDataAsync(userId) {
    var d = $.Deferred();
    getUserData(userId, function(res){ d.resolve(res); }, function(err){ d.reject(err); });
    return d.promise();
}

また、jQueryは $.Deferred(fn) を非常によく模倣した式を書くことができるという利点があります。 new Promise(fn) という形式で、以下のようになります。

function getUserDataAsync(userId) {
    return $.Deferred(function(dfrd) {
        getUserData(userId, dfrd.resolve, dfrd.reject);
    }).promise();
}

注:ここでは、jQuery遅延の resolvereject メソッドは、quot;detachable" つまり、それらは インスタンス jQuery.Deferred()を使用します。全てのLibがこの機能を提供しているわけではありません。

3. ノードスタイルコールバック("nodeback")。

ノードスタイルコールバック(nodebacks)は、コールバックが常に最後の引数で、その最初の引数がエラーになるという特殊な形式を持っています。まずは手動でプロミシングしてみましょう。

getStuff("dataParam", function(err, data) { …

へ。

function getStuffAsync(param) {
    return new Promise(function(resolve, reject) {
        getStuff(param, function(err, data) {
            if (err !== null) reject(err);
            else resolve(data);
        });
    });
}

遅延を使うと、次のようなことができます(この例ではQを使いますが、Qは新しい構文をサポートするようになりました)。 を選択する必要があります。 ):

function getStuffAsync(param) {
    var d = Q.defer();
    getStuff(param, function(err, data) {
        if (err !== null) d.reject(err);
        else d.resolve(data);
    });
    return d.promise;   
}

一般的に、あまり手動でプロミスをするべきではありません。Node 8+ のネイティブプロミスと同様に Node を考慮して設計されたほとんどのプロミスライブラリは、ノードバックをプロミスするためのメソッドを内蔵しています。例えば

var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native promises, node only

4. ノードスタイルコールバックによるライブラリ全体。

ここでは黄金律はなく、ひとつひとつプロミシングしていくのです。しかし、いくつかのプロミス実装では、これを一括して行うことができます。例えばBluebirdでは、ノードバックAPIをプロミスAPIに変換するのは以下のように簡単です。

Promise.promisifyAll(API);

または ネイティブプロミス ノード :

const { promisify } = require('util');
const promiseAPI = Object.entries(API).map(([key, v]) => ({key, fn: promisify(v)}))
                         .reduce((o, p) => Object.assign(o, {[p.key]: p.fn}), {});

注意事項

  • の時はもちろん .then ハンドラでは、プロミスをする必要はありません。プロミスを返すのは .then ハンドラは、そのプロミスの値で解決または拒否します。ハンドラから投げる .then ハンドラも良い習慣で、プロミスを拒否します。これが有名なプロミススローの安全性です。
  • 実際の onload の場合は addEventListener よりも onX .