jQuery.Deferredを使って非同期ループしよう!
突然ですが、Deferred使ってますか?
jQuery.DeferredとはjQueryのバージョン1.5から導入された、非同期処理をうまく扱うための標準モジュールです。
使いこなすことで、以下のような効果が見込めます。
- 非同期処理を連結する際、コールバック地獄から解放される(直列処理、並列処理が可能)
- エラー処理をうまく記述できる
- 一連の非同期処理を関数化して再利用しやすくできる
って説明される事が多いかと思います。
利用方法を説明してるサイトもいっぱいあります。
…が非同期に慣れてない方や、とりあえずjQueryは触れるよ!くらいの人だとすごくわかりにくい動きになっています。
jquery-asyncEach.js
if(jQuery) !function($){ 'use strict'; if( typeof $.asyncEach === 'undefined' ){ // setTimeoutとDeferredを使い非同期にする関数 var async = function($callback){ var $dfd = $.Deferred(); setTimeout(function(){ $callback($dfd); },0); return $dfd.promise(); }; // $.asyncEachメソッド本体 $.asyncEach = function($array,$callback,$thisArg){ if( typeof $thisArg === 'undefined' ) $thisArg = $array; //$arrayが大量だった時に$whenを作るのに時間がかかるのでいきなり非同期化 return async(function($dfd){ //$arrayの要素を1個づつ非同期化 var $when = $.map($array,function($value,$index){ return async(function($dfd){ return $callback.call($thisArg,$dfd,$value,$index); }); }); //$whenが全てresolveするとdone、ひとつでもrejectするとfailする return $.when.apply($,$when) .done(function(){ //argumentsを配列に変換して返す var $_ = []; $_.push.apply($_,arguments); return $dfd.resolve($_); }) .fail(function(){ var $_ = []; $_.push.apply($_,arguments); return $dfd.reject($_); }) ; }); }; } }(jQuery);
テストコード
jQuery.js、jquery-asyncEach.jsの順で読み込んで実行してください。
!function(){ console.log('最初に表示される'); $.asyncEach([1,2,3,4,5,6,7,8,9,10],function($dfd,$value,$index){ //ここに重い処理(…の、かわりにランダムで待たせてから返す) var $r = Math.floor( Math.random() * 1050 ); setTimeout(function(){ //どちらかを使って返り値を返す // $dfd.resolve() = 処理成功 // $dfd.reject() = 処理失敗 return $r <= 1000 ? $dfd.resolve($value * 2) : $dfd.reject($value + 'がキモイ'); },$r); }).then(//.done,.failでもいい function($result){ console.log('重い処理が全部終わったら呼ばれる'); console.log($result); },function($result){ console.log('途中でrejectされたら呼ばれる'); console.log($result); } ); console.log('asyncEachは非同期なので2番目に表示される'); }()