2つの非同期メソッド呼び出し、barとbazを持つプロトタイプオブジェクトFooがあります。
var bob = new Foo()
Foo.prototype.bar = function land(callback) {
setTimeout(function() {
callback()
console.log('bar');
}, 3000);
};
Foo.prototype.baz = function land(callback) {
setTimeout(function() {
callback()
console.log('baz');
}, 3000);
};
Bob.bar()。baz()を実行して、「bar」と「baz」を順にログに記録させます。
メソッドの呼び出しを変更できない場合(コールバック関数で渡すことを含む)、これらのメソッドの呼び出しにデフォルトのコールバックをどのように渡すことができますか?
いくつかのアイデア:
「ボブ」をデコレータでラップします(実装方法はまだあいまいですが、小さな例を使用できます)
何も割り当てられていない場合はデフォルトのコールバックを割り当てるようにコンストラクターを変更します(これが可能かどうかを考慮していません)。
何もなくなるまで次のメソッドを呼び出すジェネレーターラッパーを使用しますか?
代わりに、より推奨される方法は promises を使用することです。これは非同期のことをするコミュニティ全体の傾向なので。
Bob.bar()。baz()を実行して、「bar」と「baz」を順にログに記録させます。
このbob.bar().baz()
"構文"を実現するためだけに、なぜそれを実行したいのですか?構文を機能させるための追加の努力なしでPromise APIを使用してそれをかなり簡単に行うことができる場合、実際にコードを複雑にし、実際のコードを理解することを困難にします。
そのため、次のようなプロミスベースのアプローチの使用を検討することをお勧めします。これは、アプローチによって達成したものよりもはるかに高い柔軟性を提供します。
Foo.prototype.bar = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('bar');
}, 3000);
};
};
Foo.prototype.baz = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('baz');
}, 3000);
};
};
これを実行して、次のように順次実行します。
var bob = new Foo();
bob.bar().then(function() {
return bob.baz();
});
// If you're using ES2015+ you could even do:
bob.bar().then(() => bob.baz());
より多くの関数をチェーンする必要がある場合は、単純にそれを行うことができます。
bob.bar()
.then(() => bob.baz())
.then(() => bob.anotherBaz())
.then(() => bob.somethingElse());
とにかく、あなたがプロミスを使うことに慣れていないなら、あなたはあなたがしたいかもしれません これを読む
警告これはまだ正しくありません。理想的には、Promiseをサブクラス化し、適切なthen/catch機能を備えていますが、サブクラス化 bluebird Promise にはいくつかの注意事項があります。アイデアは、Promise生成関数の内部配列を格納し、Promiseが待機するとき(その後/待機する)、それらのPromiseを順番に待機することです。
const Promise = require('bluebird');
class Foo {
constructor() {
this.queue = [];
}
// promise generating function simply returns called pGen
pFunc(i,pGen) {
return pGen();
}
bar() {
const _bar = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('bar',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.Push(_bar);
return this;
}
baz() {
const _baz = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('baz',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.Push(_baz);
return this;
}
then(func) {
return Promise.reduce(this.queue, this.pFunc, 0).then(func);
}
}
const foo = new Foo();
foo.bar().baz().then( () => {
console.log('done')
})
結果:
messel@messels-MBP:~/Desktop/Dropbox/code/js/async-chain$ node index.js
bar 1492082650917
baz 1492082651511
done
コールバックの地獄を避け、正気を保ちたい場合は、関数型プログラミングのためにES6の約束が最も適切なアプローチです。同期タイムラインで作業するのと同じように、非同期タイムラインで順次非同期タスクをチェーンするだけです。
この特定のケースでは、非同期関数を約束する必要があるだけです。非同期関数がデータとasynch(data,myCallback)
のようなコールバックを受け取ると仮定します。コールバックが最初のタイプのエラーであると仮定しましょう。
といった;
var myCallback = (error,result) => error ? doErrorAction(error)
: doNormalAction(result)
Asynch関数が約束された場合、実際には、データを受け取ってpromiseを返す関数が返されます。 myCallback
の段階でthen
を適用する必要があります。次に、myCallback
の戻り値が次のステージに渡されます。このステージでは、myCallback
の戻り値が指定された別の非同期関数を呼び出すことができます。これは、必要な限り繰り返されます。それでは、ワークフローにこの抽象を実装する方法を見てみましょう。
function Foo(){}
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function myCallback(val) {
console.log("hey..! i've got this:",val);
return val;
}
var bob = new Foo();
Foo.prototype.bar = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('bar');
}, 1000);
};
Foo.prototype.baz = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('baz');
}, 1000);
};
Foo.prototype.bar = promisify(Foo.prototype.bar);
Foo.prototype.baz = promisify(Foo.prototype.baz);
bob.bar(1)
.then(myCallback)
.then(bob.baz)
.then(myCallback)