読者です 読者をやめる 読者になる 読者になる

野次馬エンジニア道

野次馬な気持ちでプログラミングをあれこれと綴ります

Promise/Deferred パターン

非同期処理の記述

Deferredとは遅延評価、Promiseとは未来に決定される約束という意味。 Promise等の名称は言語によるが、下記の説明がわかりやすい。

future, promise, delay とは、プログラミング言語における並列処理のデザインパターン。何らかの処理を別のスレッドで処理させる際、その処理結果の取得を必要になるところまで後回しにする手法。処理をパイプライン化させる。1977年に考案され、現在ではほとんどのプログラミング言語で利用可能。 (Wikipediaより引用)

jQueryの場合、Deferredオブジェクトのメソッドを呼んで、Promiseオブジェクトの状態を変える。Promiseオブジェクトは初期状態としては未解決(unresolved)。非同期処理が終わった後に、成功(resolved)か失敗(rejected)の状態に遷移し、成功(done)と失敗(fail)の処理がそれぞれ遅延実行される。

function someAsync() {
    var deferred = $.Deferred();
    setTimeout(function(){
        deferred.reject();      
    }, 1);
    return deferred.promise();
}

someAsync().done(function(data){
    // call after resolve()   
});

someAsync().fail(function(ex){
    // call after reject()  
});

then()を使うと両方一気に登録できる。

someAsync().then( function(data){
    // call after resolve()
}, function(ex){
    // call after reject() 
});

成功や失敗に関わらず常に実行する処理も記述可能。ajaxの場合

$.ajax("/test").then( function(data) {
 // success
}, function(ex) { 
 // failure
}).always(function(){
  // finally
});

と書ける。

複数Promiseの連結(jQuery)

上でみたthenを使って処理を連結できる。 順次実行する場合は、thenの中でDeferredオブジェクトを返す。

$.ajax("/a").then( function() { 
    return $.ajax("/b"); 
}).then( function() { 
    return $.ajax("/c");
});

並列に実行して完了を待ちたい場合は、

$.when(
  $.ajax("/a"),
  $.ajax("/b"),
  $.ajax("/c"),
).done(a,b,c) {
  // use a, b, c result
});

となる。

複数Promiseの連結(AngularJS)

AngularJSは、組み込みのオブジェクトして$qを提供している。

Deferredオブジェクトを$q.defer()で取得すれば、使い方はjQueryと同じ。 順次実行は同様にthenでチェーンするが並列実行は下記のように$q.allを使う。

function someAsync() {
    var defer = $q.defer();
    setTimeout(function(){
        defer.resolved();      
    }, 1);
    return defer.promise;
}

var promises = [];
promises.push(someAsync());
promises.push(someAsync());
promises.push(someAsync());

$q.all(promises);