私は Qで任意の数のプロミスをチェーニングしました ;私の質問は違います。
可変数の呼び出しを行う方法を教えてください。呼び出しはそれぞれ非同期で戻ります。
シナリオはHTTPリクエストのセットであり、その数とタイプは最初のHTTPリクエストの結果によって決定されます。
簡単にやってみたいです。
この答え も見て、これは次のようなことを示唆しています:
var q = require('q'),
itemsToProcess = ["one", "two", "three", "four", "five"];
function getDeferredResult(prevResult) {
return (function (someResult) {
var deferred = q.defer();
// any async function (setTimeout for now will do, $.ajax() later)
setTimeout(function () {
var nextResult = (someResult || "Initial_Blank_Value ") + ".." + itemsToProcess[0];
itemsToProcess = itemsToProcess.splice(1);
console.log("tick", nextResult, "Array:", itemsToProcess);
deferred.resolve(nextResult);
}, 600);
return deferred.promise;
}(prevResult));
}
var chain = q.resolve("start");
for (var i = itemsToProcess.length; i > 0; i--) {
chain = chain.then(getDeferredResult);
}
...しかし、そのようにitemsToProcessをループするのは面倒です。または、再帰を抽象化する「ループ」と呼ばれる新しい関数を定義します。より良い方法は何ですか?
_[].reduce
_ を使用してこれを行うには、すてきなクリーンな方法があります。
_var chain = itemsToProcess.reduce(function (previous, item) {
return previous.then(function (previousValue) {
// do what you want with previous value
// return your async operation
return Q.delay(100);
})
}, Q.resolve(/* set the first "previousValue" here */));
chain.then(function (lastResult) {
// ...
});
_
reduce
配列を反復処理し、前の反復の戻り値を渡します。この場合、promiseを返すので、毎回then
をチェーンします。最初の約束を(q.resolve("start")
で行ったように)提供して、物事を開始します。
最初はここで何が起こっているのかを理解するのに少し時間がかかる場合がありますが、少し時間をかけてそれを処理すると、機械をセットアップする必要がなく、どこでも簡単に使用できるパターンになります。
以下は、Q
で定義された状態マシンの概念です。
HTTP関数が定義されているとすると、Q
promiseオブジェクトが返されます。
_var Q_http = function (url, options) {
return Q.when($.ajax(url, options));
}
_
次のように再帰関数nextState
を定義できます。
_var states = [...]; // an array of states in the system.
// this is a state machine to control what url to get data from
// at the current state
function nextState(current) {
if (is_terminal_state(current))
return Q(true);
return Q_http(current.url, current.data).then(function (result) {
var next = process(current, result);
return nextState(next);
});
}
_
ここで、function process(current, result)
は、HTTP呼び出しからのcurrent
状態およびresult
に応じて次のステップがどうなるかを調べる関数です。
使用するときは、次のように使用します。
_nextState(initial).then(function () {
// all requests are successful.
}, function (reason) {
// for some unexpected reason the request sequence fails in the middle.
});
_
私にはわかりやすい別の解決策を提案します。 promiseを直接チェーンする場合と同じようにします:promise.then(doSomethingFunction).then(doAnotherThingFunction);
これをループに入れると、次のようになります。
_var chain = Q.when();
for(...) {
chain = chain.then(functionToCall.bind(this, arg1, arg2));
};
chain.then(function() {
console.log("whole chain resolved");
});
var functionToCall = function(arg1, arg2, resultFromPreviousPromise) {
}
_
function currying を使用して、複数の引数を使用します。この例では、functionToCall.bind(this, arg1, arg2)
は1つの引数を持つ関数を返します。functionToCall(resultFromPreviousPromise)
前のpromiseの結果を使用する必要はありません。
私はこの方法が好きです:
_var q = require('q'),
itemsToProcess = ["one", "two", "three", "four", "five"];
function getDeferredResult(a) {
return (function (items) {
var deferred;
// end
if (items.length === 0) {
return q.resolve(true);
}
deferred = q.defer();
// any async function (setTimeout for now will do, $.ajax() later)
setTimeout(function () {
var a = items[0];
console.log(a);
// pop one item off the array of workitems
deferred.resolve(items.splice(1));
}, 600);
return deferred.promise.then(getDeferredResult);
}(a));
}
q.resolve(itemsToProcess)
.then(getDeferredResult);
_
ここで重要なのは、_deferred.promise
_の.then()
を、ワークアイテムの配列のスプライスバージョンを使用して呼び出すことです。このthen
は、最初の据え置き約束が解決された後に実行されます。これは、setTimeoutのfnにあります。より現実的なシナリオでは、延期されたプロミスはhttpクライアントのコールバックで解決されます。
最初のq.resolve(itemsToProcess)
は、作業項目を作業fnの最初の呼び出しに渡すことで開始します。
他の人の役に立つことを願ってこれを追加しました。