promise/deferライブラリはどのように実装されていますか?[クローズド]
質問
のようなpromise/deferライブラリは、どのように q はどのように実装されているのでしょうか? ソースコードを読もうとしたのですが、かなり理解しにくかったので、NodeやブラウザのようなシングルスレッドJS環境でプロミスを実装するために使われるテクニックは何か、誰かが高いレベルから説明してくれるとうれしいと思いました。
どのように解決するのですか?
例を示すよりも説明する方が難しいと思うので、ここでは defer/promise がどのようなものかを非常に簡単に実装してみます。
免責事項です。 これは機能的な実装ではなく,Promise/A の仕様の一部が欠落しています. これは約束事の基礎を説明するためのものです.
tl;dr: に行ってください。 クラスを作成し、例 セクションで完全な実装を見ることができます。
プロミス
まず、コールバックの配列を持つプロミスオブジェクトを作成する必要があります。オブジェクトで作業を始めるのがわかりやすいからです。
var promise = {
callbacks: []
}
では、そのメソッドでコールバックを追加します。
var promise = {
callbacks: [],
then: function (callback) {
callbacks.push(callback);
}
}
そして、エラーのコールバックも必要です。
var promise = {
okCallbacks: [],
koCallbacks: [],
then: function (okCallback, koCallback) {
okCallbacks.push(okCallback);
if (koCallback) {
koCallbacks.push(koCallback);
}
}
}
延期する。
ここで、プロミスを持つことになるDeferオブジェクトを作成します。
var defer = {
promise: promise
};
deferは解決する必要があります。
var defer = {
promise: promise,
resolve: function (data) {
this.promise.okCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(data)
}, 0);
});
},
};
と拒否する必要があります。
var defer = {
promise: promise,
resolve: function (data) {
this.promise.okCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(data)
}, 0);
});
},
reject: function (error) {
this.promise.koCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(error)
}, 0);
});
}
};
コードが常に非同期であることを可能にするために、コールバックがタイムアウトで呼び出されることに注意してください。
そして、これが基本的なdefer/promiseの実装に必要なことです。
クラスとサンプルを作成します。
さて、両方のオブジェクトをクラスに変換しましょう。まずは約束です。
var Promise = function () {
this.okCallbacks = [];
this.koCallbacks = [];
};
Promise.prototype = {
okCallbacks: null,
koCallbacks: null,
then: function (okCallback, koCallback) {
okCallbacks.push(okCallback);
if (koCallback) {
koCallbacks.push(koCallback);
}
}
};
そして、今度は延期です。
var Defer = function () {
this.promise = new Promise();
};
Defer.prototype = {
promise: null,
resolve: function (data) {
this.promise.okCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(data)
}, 0);
});
},
reject: function (error) {
this.promise.koCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(error)
}, 0);
});
}
};
そして、使用例です。
function test() {
var defer = new Defer();
// an example of an async call
serverCall(function (request) {
if (request.status === 200) {
defer.resolve(request.responseText);
} else {
defer.reject(new Error("Status code was " + request.status));
}
});
return defer.promise;
}
test().then(function (text) {
alert(text);
}, function (error) {
alert(error.message);
});
見ての通り、基本的な部分はシンプルで小さいものです。他のオプション、例えば複数の約束事の解像度を追加すると、大きくなるでしょう。
Defer.all(promiseA, promiseB, promiseC).then()
またはプロミスチェイニング
getUserById(id).then(getFilesByUser).then(deleteFile).then(promptResult);
仕様の詳細を見るには CommonJS Promiseの仕様 . なお,主要なライブラリ(Q, when.js, rsvp.js, node-promise, ...)は,以下の仕様に準拠しています. プロミス/A の仕様に従います.
十分に明確であったことを望みます。
編集する
コメントで聞かれたように、このバージョンでは2つのことを追加しました。
- プロミスがどのような状態であっても、プロミスのその後を呼び出すことができるようになりました。
- プロミスをチェーンする可能性。
解決されたときにプロミスを呼び出せるようにするには、プロミスにステータスを追加し、thenが呼び出されたときにそのステータスをチェックする必要があります。ステータスが解決または拒否された場合、そのデータまたはエラーでコールバックを実行するだけです。
プロミスを連鎖させるためには
then
を生成し、プロミスが解決/拒否されたときに、コールバックの結果で新しいプロミスを解決/拒否する必要があります。つまり、プロミスが終了したとき、コールバックが新しいプロミスを返した場合、そのプロミスは
then()
. そうでない場合は、プロミスはコールバックの結果で解決されます。
これがプロミスです。
var Promise = function () {
this.okCallbacks = [];
this.koCallbacks = [];
};
Promise.prototype = {
okCallbacks: null,
koCallbacks: null,
status: 'pending',
error: null,
then: function (okCallback, koCallback) {
var defer = new Defer();
// Add callbacks to the arrays with the defer binded to these callbacks
this.okCallbacks.push({
func: okCallback,
defer: defer
});
if (koCallback) {
this.koCallbacks.push({
func: koCallback,
defer: defer
});
}
// Check if the promise is not pending. If not call the callback
if (this.status === 'resolved') {
this.executeCallback({
func: okCallback,
defer: defer
}, this.data)
} else if(this.status === 'rejected') {
this.executeCallback({
func: koCallback,
defer: defer
}, this.error)
}
return defer.promise;
},
executeCallback: function (callbackData, result) {
window.setTimeout(function () {
var res = callbackData.func(result);
if (res instanceof Promise) {
callbackData.defer.bind(res);
} else {
callbackData.defer.resolve(res);
}
}, 0);
}
};
そして、繰り下げ。
var Defer = function () {
this.promise = new Promise();
};
Defer.prototype = {
promise: null,
resolve: function (data) {
var promise = this.promise;
promise.data = data;
promise.status = 'resolved';
promise.okCallbacks.forEach(function(callbackData) {
promise.executeCallback(callbackData, data);
});
},
reject: function (error) {
var promise = this.promise;
promise.error = error;
promise.status = 'rejected';
promise.koCallbacks.forEach(function(callbackData) {
promise.executeCallback(callbackData, error);
});
},
// Make this promise behave like another promise:
// When the other promise is resolved/rejected this is also resolved/rejected
// with the same data
bind: function (promise) {
var that = this;
promise.then(function (res) {
that.resolve(res);
}, function (err) {
that.reject(err);
})
}
};
ご覧の通り、かなり大きくなりました。
関連
-
[解決済み] 配列から特定の項目を削除するにはどうすればよいですか?
-
[解決済み] jQueryで要素が非表示になっているかどうかを確認するには?
-
[解決済み] JavaScriptで文字列が部分文字列を含むかどうかを確認する方法は?
-
[解決済み] あるJavaScriptファイルを他のJavaScriptファイルにインクルードするにはどうすればよいですか?
-
[解決済み] GUID / UUIDの作成方法
-
[解決済み] JavaScriptでタイムスタンプを取得する方法は?
-
[解決済み】別のウェブページにリダイレクトするにはどうすればいいですか?
-
[解決済み】JavaScriptで文字列の出現箇所をすべて置換する方法
-
[解決済み] 無効になっている入力フィールドの値を送信する
-
[解決済み] 文字列とラベルのローカライズとグローバリゼーションのベストプラクティス【終了しました
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] JavaScript で範囲を作成する - 奇妙な構文
-
[解決済み] Javascriptによるタッチスクリーンデバイスの検出
-
[解決済み] Node.jsでbase64エンコードされた画像をAmazon S3へアップロードする
-
[解決済み] 文字列がすべて同じ部分文字列で構成されているかどうかを調べるにはどうすればよいですか?
-
[解決済み] reactのrender関数でdynamic hrefを作成するには?
-
[解決済み] Angularjs - 現在の日付を表示する
-
[解決済み] CORS OriginヘッダーとCSRFトークンによるCSRF保護
-
[解決済み] JavaScriptのtoString()関数をオーバーライドして、デバッグ用に意味のある出力を提供することは可能でしょうか?
-
[解決済み] javascriptで文字列から関数を作成する方法はありますか?
-
[解決済み] Chrome拡張機能:popup.htmlを強制終了させる