Asyncの例
34978 ワード
住所:https://github.com/caolan/async Asyncの内容は三つの部分に分けられます.フロー制御:一般的なプロセスの10種類を簡略化する処理 セットの処理:非同期の操作を使用してセット内のデータを処理する方法 工具類:いくつかの常用工具類 本論文では、最も簡単で一般的なフロー制御部分を紹介します.nodejsは非同期プログラミングモデルなので、同期プログラミングで簡単にできることがありますが、今は大変です.Asyncのフロー制御はこれらの操作を簡略化するためです.
series(task,[calback])(複数の関数が順次実行されますが、データ交換はありません)
複数の非同期関数が順次呼び出され、一つの完了後に次のステップが実行されます.各関数間にはデータの交換がありません.順序を保証するだけです.この場合はseriesが使えます.純jsコード:
parallel(tasks,[calback])(複数の関数を並列に実行)
複数の関数を並行して実行します.各関数は直ちに実行されます.他の関数が先に実行されるのを待つ必要はありません.最終的なcalbackに送られる配列のデータは、完了した順序ではなくtaskで宣言された順序に従います.ある関数が間違っていたら、すぐにerrと実行済みの関数の結果値をparallelの最終的なcalbackに伝えます.他の実行されていない関数の値は最終データには届きませんが、位置を占めます.json形式のtaskysをサポートし、最終的にlbackをcalbackした結果もJson形式です.サンプルコード:
waterfall(tasks,[calback])(複数の関数が順次実行され、前の出力は次の入力となります.)
seiresと同様に、複数の関数を順次実行します.それぞれの関数で生成された値は、次の関数に伝えられます.途中でエラーが発生したら、後の関数は実行されません.エラーメッセージと以前に発生した結果は、waterfallの最終的なcalbackに伝えます.この関数はwaterfallといい、滝が上から下にかけて、途中でいくつもの突起がある石を突き進んでいくのが想像できます.なお、この関数はjson形式のtaskをサポートしていません.
aut(task,[calback])(複数の関数は依存関係があり、並列実行もあり、順次実行もあります)
依存関係のある複数のタスクを処理するために使用されます.例えば、いくつかのタスクの間で独立して、並行して実行できます.しかし、いくつかのタスクは他のいくつかのタスクに依存しています.それらのタスクが完了するまで待つしかないです.私たちはasync.parallelとasync.seriesを使ってこの機能を実現することができますが、ミッション間の関係が複雑であれば、コードはかなり複雑になります.これからは新しいタスクを追加したいなら、面倒です.この時にasync.atotを使うと、それは半ば功を奏します.ミッションの途中でエラーが発生したら、そのエラーを最後のcalbackに伝えます.実行済みのデータも含めて全てのタスクが無視されます.ここで私がプログラムを書くと、次のいくつかのことを完了します.どこでデータを取得してハードディスクに新しいディレクトリを作って、データをカタログの下のファイルに書き込んでメールを送ります.ファイルを添付して他の人に送ります.このタスクを分析すると、1と2が並行して実行できます.3は1と2が必要です.4は3が必要です.
whilst(test,fn,calback)
whileに相当しますが、その中の非同期呼び出しは完了後に次のサイクルが行われます.以下の例を示します
until(test,fn,calback)(whileと似ていますが、判定条件は逆です.)
queue(ワーカーの数を設定できるキュー)
queueは強化版のparallelに相当します.主にworkerの数量を制限しています.もう一回で全部実行しません.ワーカーの数が足りない時は、新しいワーカーが利用できるまで並んで待ちます.この関数には、ワーカーが使い終わった時、待ち時間がない時、全部実行した時など、複数の点があります.queueを定義します.そのworkerの数は2です.そして、タスク実行時にログを記録してください.
iterator(taskys)(いくつかの関数をiteratorとして包装します.)
一組の関数を一つのiteratorに包装し、next()を通して次の関数を起点とする新しいiteratorを得ることができます.この関数は主にasyncによって内部で使用されますが、必要なときにはコードでも使用できます.
apply(function,argments.)(関数へのプリバインディングパラメータ)
applyは非常に有用な関数であり、関数に複数のパラメータをプリバインし、直接に呼び出すことができる新しい関数を生成し、コードを簡略化することができます.関数について:
nextTick(calback)
nextTickの役割はnodejsのnextTickと同じで、ある関数を呼び出して列の最後に置くのです.ただし、ブラウザ側では、setTimeout(calback,0)しか使えませんが、この方法は他の優先度の高いジョブを先に挿入する場合があります.このnextTickを提供して、同じコードをサーバー側とブラウザ側で表現させます.
series(task,[calback])(複数の関数が順次実行されますが、データ交換はありません)
複数の非同期関数が順次呼び出され、一つの完了後に次のステップが実行されます.各関数間にはデータの交換がありません.順序を保証するだけです.この場合はseriesが使えます.純jsコード:
step1(function(err, v1) {
step2(function(err, v2) {
step3(function(err, v3) {
// do somethig with the err or values v1/v2/v3
}
}
});
その中からこのネストが見られますか?それとも比較的深いものですか?もう少し多く歩けばもっと深いです.コードの中で無視して、各レイヤーのerrの処理をおろそかにして、さもなくばまたif return calbackをプラスして、それでは更に面倒になりました.この場合、asyncを使って処理するということです.var async = require(‘async’) async.series([step1, step2, step3],
function(err, values) {
// do somethig with the err or values v1/v2/v3
});
コードが多く簡潔であり、各コールバックのエラーを自動的に処理することができます.もちろん、ここで一番簡単な例だけをあげます.実際には、各ステップでいくつかの操作を行います.var async = require(‘async’) async.series([function(cb) {
step1(function(err, v1) {
// do something with v1
cb(err, v1);
}),
function(cb) {
step2(...)
},
function(cb) {
step3(...)
}],
function(err, values) {
// do somethig with the err or values v1/v2/v3
});
関数の詳細は、次の関数を実行するために、関数の各配列を順次実行します.各関数が実行された後に、次の関数が実行されます.任意の関数がそのコールバック関数に一つのerrorを伝えたら、後の関数は実行されず、すぐにこのerrorと実行された関数の結果をseriesの最後のcalbackに伝えます.すべての関数が実行された後(エラーはありません)、各関数がそのコールバック関数に伝えられた結果を一つの配列に統合して、seriesの最後のcalbackに伝えます.また、jsonの形式でtaskysを提供することもできます.各属性は関数として実行されます.結果はjson形式でseriesの最後のcalbackにも伝えられます.このような方式のほうが可読性が高いです.具体例は参照できます.https://github.com/freewind/async_demo/blob/master/series.js そのコードには、中間の関数が間違っていたら、series関数はどのように処理しますか?もしある関数がコールバックの値をundefined、null、{}、[]などに伝えたら、seriesはどう処理しますか?parallel(tasks,[calback])(複数の関数を並列に実行)
複数の関数を並行して実行します.各関数は直ちに実行されます.他の関数が先に実行されるのを待つ必要はありません.最終的なcalbackに送られる配列のデータは、完了した順序ではなくtaskで宣言された順序に従います.ある関数が間違っていたら、すぐにerrと実行済みの関数の結果値をparallelの最終的なcalbackに伝えます.他の実行されていない関数の値は最終データには届きませんが、位置を占めます.json形式のtaskysをサポートし、最終的にlbackをcalbackした結果もJson形式です.サンプルコード:
async.parallel([function(cb) {
t.fire('a400', cb, 400)
},
function(cb) {
t.fire('a200', cb, 200)
},
function(cb) {
t.fire('a300', cb, 300)
}],
function(err, results) {
log(’1 err: ‘, err); // -> undefined
log(’1 results: ‘, results); // ->[ 'a400', 'a200', 'a300' ]
});
途中エラーの例:async.parallel([function(cb) {
log('1: ', 'start');
t.fire('a400', cb, 400)
},
// callback,
function(cb) {
log('2: ', 'start');
t.err('e200', cb, 200)
},
function(cb) {
log('3: ', 'start');
t.fire('a100', cb, 100)
}],
function(err, results) {
log(’2 err: ‘, err); // -> e200
log(’2 results: ‘, results); // -> [ , undefined, 'a100' ]
});
json形式でtaskysに伝えられました.async.parallel({
a: function(cb) {
t.fire(‘a400′, cb, 400)
},
b: function(cb) {
t.fire(‘c300′, cb, 300)
}
},
function(err, results) {
log(’3 err: ‘, err); // -> undefined
log(’3 results: ‘, results); // -> { b: ‘c300′, a: ‘a400′ }
});
より詳細な例を参照してください.https://github.com/freewind/async_demo/blob/master/parallel.jswaterfall(tasks,[calback])(複数の関数が順次実行され、前の出力は次の入力となります.)
seiresと同様に、複数の関数を順次実行します.それぞれの関数で生成された値は、次の関数に伝えられます.途中でエラーが発生したら、後の関数は実行されません.エラーメッセージと以前に発生した結果は、waterfallの最終的なcalbackに伝えます.この関数はwaterfallといい、滝が上から下にかけて、途中でいくつもの突起がある石を突き進んでいくのが想像できます.なお、この関数はjson形式のtaskをサポートしていません.
async.waterfall([function(cb) {
log('1: ', 'start');
cb(null, 3);
},
function(n, cb) {
log('2: ', n);
t.inc(n, cb);
},
function(n, cb) {
log('3: ', n);
t.fire(n * n, cb);
}],
function(err, result) {
log(’1 err: ‘, err); // -> null
log(’1 result: ‘, result); // -> 16
});
より詳細な例を参照してください.https://github.com/freewind/async_demo/blob/master/waterfall.jsaut(task,[calback])(複数の関数は依存関係があり、並列実行もあり、順次実行もあります)
依存関係のある複数のタスクを処理するために使用されます.例えば、いくつかのタスクの間で独立して、並行して実行できます.しかし、いくつかのタスクは他のいくつかのタスクに依存しています.それらのタスクが完了するまで待つしかないです.私たちはasync.parallelとasync.seriesを使ってこの機能を実現することができますが、ミッション間の関係が複雑であれば、コードはかなり複雑になります.これからは新しいタスクを追加したいなら、面倒です.この時にasync.atotを使うと、それは半ば功を奏します.ミッションの途中でエラーが発生したら、そのエラーを最後のcalbackに伝えます.実行済みのデータも含めて全てのタスクが無視されます.ここで私がプログラムを書くと、次のいくつかのことを完了します.どこでデータを取得してハードディスクに新しいディレクトリを作って、データをカタログの下のファイルに書き込んでメールを送ります.ファイルを添付して他の人に送ります.このタスクを分析すると、1と2が並行して実行できます.3は1と2が必要です.4は3が必要です.
async.auto({
getData: function(callback) {
setTimeout(function() {
console.log(’1 : got data’);
callback();
},
300);
},
makeFolder: function(callback) {
setTimeout(function() {
console.log(’1 : made folder’);
callback();
},
200);
},
writeFile: ['getData', 'makeFolder',
function(callback) {
setTimeout(function() {
console.log('1: wrote file');
callback(null, 'myfile');
},
300);
}],
emailFiles: ['writeFile',
function(callback, results) {
log('1: emailed file: ', results.writeFile); // -> myfile
callback(null, results.writeFile);
}]
},
function(err, results) {
log(’1 : err: ‘, err); // -> null
log(’1 : results: ‘, results); // -> { makeFolder: undefined,
// getData: undefined,
// writeFile: ‘myfile’,
// emailFiles: ‘myfile’ }
});
詳細例を参照してください.https://github.com/freewind/async_demo/blob/master/aut.jswhilst(test,fn,calback)
whileに相当しますが、その中の非同期呼び出しは完了後に次のサイクルが行われます.以下の例を示します
var count1 = 0;
async.whilst(function() {
return count1 < 3
},
function(cb) {
log(’1 count: ‘, count1);
count1++;
setTimeout(cb, 1000);
},
function(err) {
// 3s have passed
log(’1 err: ‘, err); // -> undefined
});
これは以下に相当しますtry {
whilst(test) {
fn();
}
callback();
} catch(err) {
callback(err);
}
この関数の機能は比較的簡単で、条件変数は通常外部に定義されています.各関数にアクセスできます.サイクルの中で、非同期呼出時に発生した値は実際に破棄されました.最後のcalbackはエラー情報しか入ってこないからです.さらに、第二の関数fnは、エラーまたは正常終了を表すために、関数cbを受け入れる必要があります.より詳細な例を参照してください.https://github.com/freewind/async_demo/blob/master/whilst_until.jsuntil(test,fn,calback)(whileと似ていますが、判定条件は逆です.)
var count4 = 0;
async.until(function() {
return count4 > 3
},
function(cb) {
log(’4 count: ‘, count4);
count4++;
setTimeout(cb, 200);
},
function(err) {
// 4s have passed
log(’4 err: ‘, err); // -> undefined
});
最初の関数条件がfalseである場合は、2番目の関数を実行し続けます.queue(ワーカーの数を設定できるキュー)
queueは強化版のparallelに相当します.主にworkerの数量を制限しています.もう一回で全部実行しません.ワーカーの数が足りない時は、新しいワーカーが利用できるまで並んで待ちます.この関数には、ワーカーが使い終わった時、待ち時間がない時、全部実行した時など、複数の点があります.queueを定義します.そのworkerの数は2です.そして、タスク実行時にログを記録してください.
var q = async.queue(function(task, callback) {
log(‘worker is processing task: ‘, task.name);
task.run(callback);
}, 2);
ウォーカーの数がなくなると、saturated関数が起動されます. q.saturated = function() {
log(‘all workers to be used’);
}
最後のタスクをワーカーに実行させると、empy関数が起動されます. q.empty = function() {
log(‘no more tasks wating’);
}
すべてのタスクが完了すると、drain関数が起動されます. q.drain = function() {
console.log(‘all tasks have been processed’);
}
複数のタスクを入れて、1回に1つ、または1回に複数のタスクを置くことができます.q.push({
name: ’t1′,
run: function(cb) {
log(‘t1 is running, waiting tasks: ‘, q.length());
t.fire(‘t1′, cb, 400); // 400ms
}
},
function(err) {
log(‘t1 executed’);
});
q.push([{
name: 't3',
run: function(cb) {
log('t3 is running, waiting tasks: ', q.length());
t.fire('t3', cb, 300); // 300ms
}
},
{
name: 't4',
run: function(cb) {
log('t4 is running, waiting tasks: ', q.length());
t.fire('t4', cb, 500); // 500ms
}
}],
function(err) {
log(‘t3 / 4 executed’);
});
詳細例を参照してください.https://github.com/freewind/async_demo/blob/master/queue.jsiterator(taskys)(いくつかの関数をiteratorとして包装します.)
一組の関数を一つのiteratorに包装し、next()を通して次の関数を起点とする新しいiteratorを得ることができます.この関数は主にasyncによって内部で使用されますが、必要なときにはコードでも使用できます.
var iter = async.iterator([function() {
console.log('111')
},
function() {
console.log('222')
},
function() {
console.log('333')
}]);
console.log(iter());
console.log(iter.next());
直接に()を呼び出すと、現在の関数が実行され、次の関数から始点となる新しいiteratorが返されます.next()を呼び出しても、現在の関数は実行されません.次の関数から出発点となる新しいiteratorに直接戻ります.同じiteratorに対して、next()を何度も呼び出しても、自分に影響はありません.要素が一つしか残っていない場合は、next()を呼び出してnullに戻ります.より詳細な例を参照してください.https://github.com/freewind/async_demo/blob/master/iterator.jsapply(function,argments.)(関数へのプリバインディングパラメータ)
applyは非常に有用な関数であり、関数に複数のパラメータをプリバインし、直接に呼び出すことができる新しい関数を生成し、コードを簡略化することができます.関数について:
function(callback) { t.inc(3, callback); }
appyで書き換えることができます. async.apply(t.inc, 3);
いくつかの関数のプリセット値も与えられます.新しい関数が得られます. var log = async.apply(console.log, ">");
log(‘hello’);
// > hello
詳細コード参照:https://github.com/freewind/async_demo/blob/master/appy.jsnextTick(calback)
nextTickの役割はnodejsのnextTickと同じで、ある関数を呼び出して列の最後に置くのです.ただし、ブラウザ側では、setTimeout(calback,0)しか使えませんが、この方法は他の優先度の高いジョブを先に挿入する場合があります.このnextTickを提供して、同じコードをサーバー側とブラウザ側で表現させます.
var calls = [];
async.nextTick(function() {
calls.push(‘two’);
});
calls.push(‘one’);
async.nextTick(function() {
console.log(calls); // -> [ 'one', 'two' ]
});
詳細コード参照:https://github.com/freewind/async_demo/blob/master/nextTick.js