自分用の co-foreach を作った話
最近nodejs触り始めた,楽しい.
co-foreachもうあるけど,自分好みじゃないんで自分用のやつ作った話.
coの中で,forEachしてその中でyieldしたいとする.
'use strict'; var co = require('co'); var request = require('co-request'); // サイトのタイトルを取ってくる co(function*(){ var urlList = [ 'http://example.com', 'http://google.com', ]; urlList.forEach(function*(url) { var res = yield request.get(url); var match = res.body.match(/<title>([\s\S]*?)<\/title>/); if (match) console.log(match[1].trim()); }); process.exit(); });
これはうまくいかない.何も表示されない.
そこで,co-foreachが既にあるので使ってみる.
'use strict'; var co = require('co'); var request = require('co-request'); var foreach = require('co-foreach'); co(function*(){ var urlList = [ 'http://example.com', 'http://google.com', ]; yield foreach(urlList, function*(url) { var res = yield request.get(url); var match = res.body.match(/<title>([\s\S]*?)<\/title>/); if (match) console.log(match[1].trim()); }); process.exit(); });
結果は次のようになる.
Google Example Domain
お分かりいただけるだろうか,順序が逆である.
これは,配列の順序が逆になっているわけではない.レスポンスが早い順のようである.
試しに次のコードを走らせてみるといい.
'use strict'; var co = require('co'); var request = require('co-request'); var wait = require('co-wait'); var foreach = require('co-foreach'); co(function*(){ var urlList = [ 'http://example.com', 'http://google.com', ]; yield foreach(urlList, function*(url) { // Googleにアクセスする前に1秒待つ if (url.match(/google/)) yield wait(1000); var res = yield request.get(url); var match = res.body.match(/<title>([\s\S]*?)<\/title>/); if (match) console.log(match[1].trim()); }); process.exit(); });
すると結果はこうなる.
Example Domain Google
つまり,並列処理(っぽく)なっているのだ.
これは困る.とても困る.誰が並列処理しろといった.
さて,今回ぼくが欲しかったのはcoのyieldが効いていて,逐次処理するforEachなわけだ.
そこでぼくは僕のためにco-foreachを作った.
それを使うとこうなる.
'use strict'; var co = require('co'); var request = require('co-request'); var foreach = require('co-foreach'); co(function*(){ var urlList = [ 'http://example.com', 'http://google.com', ]; yield urlList.forEach(function*(url) { var res = yield request.get(url); var match = res.body.match(/<title>([\s\S]*?)<\/title>/); if (match) console.log(match[1].trim()); }); process.exit(); });
結果は以下の通り.
Example Domain Google
ちゃんと順番通りである.よろしい.
もちろん,逐次処理なわけだから先程のコードより全体にかかる時間が長くなるが,ぼくがやりたいのは逐次処理なのでいいのだ.
ちなみにGithubにあげておいた.まだnpmには登録していない.(いい名前が思いつけば上げる)
インストールする時は,npm install 3846masa/co-foreach
でできる.
追記
co-rega-foreachになった.
余談だが,これはArray.prototype.forEachを書き換えている.
もちろん,通常通りのforEachも使える.generator functionかそうでないかで挙動を分けている.
そこは心配しなくて大丈夫.
'use strict'; var foreach = require('co-foreach'); var urlList = [ 'http://example.com', 'http://google.com', ]; urlList.forEach(function(url) { console.log(url); }); process.exit();
http://example.com http://google.com
for文と添字で回せばいいだろって?
Perlプログラマには、リストを反復処理するときに、C言語風のforループと添え字を使うのを避けようとする強い傾向がある。
覚えとくといい.