並列約束の流動的な構築
-
21-12-2019 - |
質問
私の質問は、次のプロミスのエレガントな並列化についてです。 青い鳥 プロミスを構築する関数にコンテキストと引数の両方を渡す必要がある場合。
私の問題を理解し、テストしやすくするために、依存関係のない例を作成しました。
計算 ( 1/(xバツx) + 1/(x*x) ) (リソースを解放する必要がある) 非同期「コンピューター」が関係します。平方と立方体は非同期かつ独立して計算されます。
次のように計算を行うことができます。
InitComputer(2) // returns a promise
.then(invert)
.then(function(arg){
return Promise.all([
proto.square(arg),
proto.cube(arg)
]);
}).spread(function(sq, cu){
this.set(sq + cu);
}).catch(function(err){
console.log('err:', err);
}).finally(endComputer);
しかし、私はこの使用法を見つけました all
理論的に可能なものと比較して重すぎます。関数を引数として渡すと、 then
, 、実行されます。関数を渡すとき all
, 、そうではありません、エラーがあります。ユーティリティまたはパターンが不足していると思われます...
このスタイルでよりシンプルなものに変更する解決策はありますか?
InitComputer(2)
.then(invert)
.all([
proto.square,
proto.cube
]).spread(function(sq, cu){
this.set(sq + cu);
}).catch(function(err){
console.log('err:', err);
}).finally(endComputer);
?
おそらくハッキングできるだろう Promise.prototype.all または、ポリモーフィズムの増加を避けるために新しい関数を定義しますが、私が興味があるのは、所有していないオブジェクトの変更を含まないソリューションのみです。
付録:
私のテストでの「コンピューター」の定義は次のとおりです。
var Promise = require("bluebird");
function Computer(){}
function InitComputer(v){
// initializing a computer is asynchronous and may fail hence the promise
var c = new Computer(), resolver = Promise.defer();
setTimeout(function(){
if (v>1) resolver.resolve(v);
else resolver.reject(new Error("bad value: "+v));
},100);
return resolver.promise.bind(c);
}
var proto = Computer.prototype;
proto.square = function(x){
// imagine this really uses the computer and is asynchronous
if (!this instanceof Computer) throw new Error('not a computer');
return x*x
}
proto.cube = function(x){ return x*x*x }
proto.set = function(v){ this.value = v }
function endComputer(){
// releases resources here
console.log('value:', this.value);
}
// this asynchronous function doesn't involve or know the computer
function invert(v){ return 1/v }
解決
そこにPromise.all
を使用する必要はありません。する代わりに:
.then(function(arg){
return Promise.all([
proto.square(arg),
proto.cube(arg)
]);
}).spread(...
.
単に使用することができます:
.then(function(arg){
return [proto.square(arg), proto.cube(arg)];
}).spread(...
.
Node.jsで矢印関数があれば、
のように単純なものになるでしょう。.then(arg => [proto.square(arg), proto.cube(arg)]).spread(...
.
Promise.all
は、 Start を必要とするときに使用されます。例えば:
var promise1 = somePromise();
var promise2 = somePromise2();
// Start the chain here
Promise.all([promise1, promise2])
.spread(function(value1, value2) {
// ...
});
. 他のヒント
あなたが言及しているようなリソース管理のユースケースについては、bluebird には Promise.using()
. Promise.using()
セットアップできます disposer()
使用終了時に非同期に取得したリソースを自動的に閉じる関数
Promise.join()
の結果を組み合わせるのも役立ちます。 cube
そして square
非同期メソッド
ここで私はあなたのことを少し書き直しました InitComputer
これがどのように機能するかを示す例です。今度は、 Computer
valの代わりにvalをプロパティとして追加したインスタンスを配置しました endComputer
プロトでも
注記:いつでも使用できます Promise.method()
遅延を返す代わりに次のようにします。
var invert = Promise.method(function invert(v){ return 1/v })
新しい initComputer:
function InitComputer(v){
var c = new Computer(), resolver = Promise.defer();
setTimeout(function(){
if (v>1) {
c.val = v;
resolver.resolve(c);
}
else resolver.reject(new Error("bad value: "+v));
},100); /** notice resource disposer function added below **/
return resolver.promise.bind(c).disposer(function(compu){compu.endComputer()});
}
新しいコード:
Promise.using(InitComputer(1.2), function(computer){
return invert(computer.val)
.then(function(inverted){
return Promise.join(computer.square(inverted), computer.cube(inverted),
function(sq, cu){
computer.set(sq + cu)
}
)
})
.catch(function(err){
console.log('err:', err);
});
})