async enhancement
TRANSCRIPT
Async EnhancementGrand-Frontend-Osaka 2015 Summer
2015.8.22
自己紹介• かみやん (Twitter@kamiyam)
http://nantokaworks.com
• 主にJavaScriptでお仕事をしている人
• カメラ/自動車
Engineer
AGENDA• JavaScriptと非同期処理 • 非同期の強みと弱み • 非同期処理実行のエンドポイント • ライブラリコールバック • 非同期処理中のエラーハンドリング • 複数非同期処理のコールバック
AGENDA• 非同期処理実行時のscopeのお話 • Promise • 革命的だった async モジュール • Promise準拠 • Q • まとめ
JavaScriptと非同期処理
同期処理
2sec
5sec
1sec
同期処理
2sec
5sec
1sec
2sec
5sec
非同期処理
1sec
2sec
5sec
非同期処理
1sec
非同期の強みと弱み
// sorry node.js codevar fs = require(“fs”);
// Sync read directoryvar files = fs.readdirSync(“.”);console.log(files);console.log(“conmplate”);
// Async read directoryfs.readdir(“.", function(err, files){ if (err) throw err; console.log(files);});console.log(“complate…?”);
プログラムのメインフローを邪魔せず非同期で処理をnon blockingで実行
(する代わりに人が追いにくい流れに)
ドミノ倒しを想像するとわかりやすい
非同期処理実行の エンドポイント
// document ready$(function(){ var $target = $(".initAnimetion”); $target.fadeIn( "slow", function() { setTimeout(function(){ $target.fadeOut( "slow", function() { console.log( "completed!!!" ); }) }), 2*1000 ); //2秒待つ });});
非同期の処理が終わった時に実行されるコールバック関数が必要
ライブラリコールバック
処理終了後の振る舞いは利用者に委ねなければならない=コールバック関数が常に必要
// example(function($){$.mySuperElegantLibrary = (function(){var hoge = “”;var fuga = “”;return {miracleMethod: function(arg1,cb){if(typeof(arg1) === 'function') cb = arg1;if((cb == null)) cb = $.noop;
},specialMethod: function(arg1,cb){if(typeof(arg1) === 'function') cb = arg1;if((cb == null)) cb = $.noop;
}}
})();})(jQuery);
変数 cb は関数なのか文字列なのかundefinedなのかそうじゃないのか…
つきまとうコールバックとの闘い
非同期処理中の エラーハンドリング
JavaScript にも try…catch あります
// example catching error(function(){try{
throw new Error("エラーです");
}catch(e){ console.log(e); console.log("ここで正しい処理をします")}
})();
// example catching error…???// callback method inner is wonder land
(function(){try{ setTimeout(function(){ throw new Error("エラーです"); },1000);}catch(e){ console.log(e); console.log("ここでただしい処理をしますし(時間差)");}})();
oh……
複数非同期処理の コールバック
// async methodsvar A = function(cb){ setTimeout(function(){ cb("1秒掛かる処理を実行しました"); },1000);};
var B = function(cb){ setTimeout(function(){ cb("5秒掛かる処理を実行しました"); },5000);};
var C = function(cb){ setTimeout(function(){ cb("2秒掛かる処理を実行しました"); },2000);};
MISSION
処理A,B,C それぞれの結果をA,B,Cの順に配列に入れよ
//oh! very easyvar result =[];
A(function(message1){
console.log(message1); result.push(message1);
B(function(message2){
console.log(message2); result.push(message2);
C(function(message3){
console.log(message3); result.push(message3); console.log("全ての処理がおわりました"); console.log(result); });
});});
同期処理
2sec
5sec
1sec
同期処理
2sec
5sec
1sec
// parallelsvar allRequest = [A,B,C];var result =[];var counter = allRequest.length;
allRequest.forEach(function(request,i){
console.log(i); // 0,1,2 request(function(message){
// Message出力(単体) console.log(message); result[i] = message;
console.log(i); //0,2,1 <—-?
if(--counter == 0){ // Message出力(全体) console.log("全ての処理がおわりました"); console.log(result); }
});});
2sec
5sec
非同期処理
1sec
2sec
5sec
非同期処理
1sec
直列同期処理処理内容 処理時間
処理①
処理②
処理③
完了
直列同期処理処理内容 処理時間
処理①
処理②
処理③
※ 前述サンプルのような処理の結果を以って次の処理を行う場合は別
同時に実行出来る処理は実行してしまいたい完了
並列同期処理処理内容 処理時間
処理①
処理②
処理③
完了
並列同期処理処理内容 処理時間
処理①
処理②
処理③
完了
それぞれの処理を同時に実行し、一番遅い処理が完了するまで他は待機
ここで一旦話は変わり…
非同期処理実行時の scopeのお話
JavaScript ループで回した時、最後の方の要素しかなんか動かない問題
// no problem(function($){$(function(){
// display none $("#content").children().hide();
//forEach$("#content").children().each(function(i,item){
// show $(item).fadeIn("slow");
})
})
})(jQuery)
// no problem(function($){ $(function(){
// display none $("#content").children().hide(); //forEach $("#content").children().each(function(i,item){ // show setTimeout(function(){ $(item).fadeIn("slow"); }, i * 1000);
})
})
})(jQuery)
大丈夫そう
// problems happen…// why!!! JavaScript looping!!(function($){ $(function(){
// display none $("#content").children().hide();
var children = $("#content").children(); for(var i=0; i < children.length; i++){ //for loop
// show setTimeout(function(){ var item = children[i]; $(item).fadeIn("slow"); }, i * 1000);
}
})
})(jQuery)
// changing code…(function($){ $(function(){
// display none $(“#content”).children().hide(); var show = function(i){ // show setTimeout(function(){ var item = children[i]; $(item).fadeIn("slow"); }, i * 1000); }
var children = $("#content").children(); for(var i=0; i < children.length; i++){ //for loop show(i) }
})
})(jQuery)
// before parallels processvar allRequest = [A,B,C];var result =[];var counter = allRequest.length;
for(var i=0; i<allRequest.length; i++){
console.log(i); // 0,1,2 allRequest[i](function(message){
// Message出力(単体) console.log(message); result[i] = message;
console.log(i); //3,3,3 <- !!!
if(--counter == 0){ // Message出力(全体) console.log("全ての処理がおわりました"); console.log(result); }
});}
くろーじゃー…
Promise
複数の非同期処理の最後もうちょっとなんとかなりませんか?
成功と失敗への遷移の確約
Promises/A+https://promisesaplus.com/
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
MDN Promise
http://caniuse.com/#feat=promises
Can I Use
try~catchはエラーの確約だったはず…
// promise…then…catchvar promise = new Promise(function(resolve,reject){// example async progressdoSomhing(function(error, data){if(error) return reject(error);resolve(data);})
});
promise.then(function(data){// with success}).catch(function(error){// fail…})
// parallel…then…catchvar promise1 = new Promise(function(resolve,reject){
// example async progressdoSomhing(function(error, data1){if(error) return reject(error);resolve(data1);
})});
var promise2 = new Promise(function(resolve,reject){// 省略resolve(data2)
});
Promise.all([promise1,promise2]).then(function(result){// with successconsole.log(result); // [data1,data2]
}).catch(function(error){// fail…
})
resolve / reject に結果の約束を結びつける
革命的だった async モジュール(私の中では)
async.waterfall([function(next){console.log(“1”);
setTimeout(function(){console.log(“1 complete”);next(null, 1); //next(error, result);
},3000);},function(next){console.log(“2”)
setTimeout(function(){console.log(“2 complete”);next(null, 2); //next(error, result);
},3000);}], function(err, result1, result2 ){console.log(err); // …nullconsole.log(result1); //…1console.log(result2); //…2
});
async. parallel([function(callback){console.log(“1”);
setTimeout(function(){console.log(“1 complete”);callback(null, 1); //arg1…error,arg2….result
},3000);},function(next){console.log(“2”)
setTimeout(function(){console.log(“2 complete”);callback(null, 2);
},1000);}], function(err, result){console.log(err); // …nullconsole.log(result); //…[“1”,”2”]
});
Promise準拠
JavaScript Promises ※ 公開日 2013/12/16
http://www.html5rocks.com/ja/tutorials/es6/promises/
Conformant Implementationshttps://promisesaplus.com/implementations
Q
// promise…then…catchvar promise = function(){
var deferred = q.deferred();// example async progressdoSomething(function(error, data){if(error) return deferred.reject(error);deferred.resolve(data);
})return deferred.promise;
});
promise.then(function(result){// with successconsole.log(result); // data
}).catch(function(error){// fail…
})
// parallel…then…catchvar promise1 = function(){
var deferred = q.deferred();// set deferred.reject(data1) & deferred.resolve(error)return deferred.promise;
});
var promise2 = function(){var deferred = q.deferred();// set deferred.reject(data2) & deferred.resolve(error)return deferred.promise;
});
q.all([promise1(), promise2()]).then(function(result){// with successconsole.log(result); // [data1,data2]
}).catch(function(error){// fail…
})
まとまらないまとめ
非同期処理が複数実行される時、その実行と完了を追うことが非常に重要
Promise準拠ライブラリをうまく使い分けて半同期処理的なアプローチを
他にGenerator / Yield もあるよ
参考• JavaScriptと非同期のエラー処理 http://techblog.yahoo.co.jp/programming/javascript_error/
• あなたの知らない JavaScript Promise https://gist.github.com/kuu/e182d361520e70a158c9
• Effective ES6http://www.slideshare.net/teppeis/effective-es6
ご清聴ありがとうございました