Skip to content

Instantly share code, notes, and snippets.

@ashikawa
Created November 12, 2012 04:06
Show Gist options
  • Save ashikawa/4057452 to your computer and use it in GitHub Desktop.
Save ashikawa/4057452 to your computer and use it in GitHub Desktop.
JS会勉強会資料2 Deferred

$.Deferred()

最近、急に増えてきた非同期処理

  • script タグの async 属性
  • 画像 onload
  • FileAPI
  • Ajax
  • IndexedDB
  • Web workers
  • GeoLocation API
  • Animation (CSS/jQuery)

この辺をすっきり書くためのフレームワーク(?), $.Deferred()
「すっきり書く」以上の機能は無いので、無くても良い物です。

以下、旧来の書き方と、$.Deferredの違いを比較しながら
( 細かい使い方については、あまり説明しませんので悪しからず )

シンプルな Ajax 通信

  • 今までの書き方
$.ajax({
    url: './dummy.json',
    type: 'GET',
    dataType: 'json',
    complete: function (xhr, textStatus) {
        //called when complete
        console.log(arguments);
    },
    success: function (data, textStatus, xhr) {
        //called when successful
        console.log(arguments);
    },
    error: function (xhr, textStatus, errorThrown) {
        //called when there is an error
        console.log(arguments);
    }
});
  • Deferredを使った書き方
$.ajax({
    url: './dummy.json',
    type: 'GET',
    dataType: 'json'
}).done(function () {
    // called when success
    console.log(arguments);
}).fail(function () {
    // called when error
    console.log(arguments);
}).always(function () {
    // called when always
    console.log(arguments);
});

あんま変わらん。

自分で非同期処理を定義

  • コールバックを引数で投げ込む
function waitCallback(sec, options, callback) {
    setTimeout(function () {
        callback();
    }, sec * 1000);
}

waitCallback(3, {}, function () {
    console.log(arguments);
});
  • コールバックをDeferrdで
function waitDeferred(sec, options) {

    var deferred = $.Deferred();

    setTimeout(function () {
        deferred.resolve(options);
    }, sec * 1000);

    return deferred.promise();
}

waitDeferred(3, {}).done(function (data) {
    console.log(arguments);
});

逆に長くなった。

promise について

promise で返されるオブジェクトは、Deferred から resolved, rejected のメソッドを使えなくした物。 「初期化/セッティングが完了して、後はイベントの実行待ち」の意味。

複数の非同期処理を連携させる

処理1が終わったら処理2

waitCallback(3, {}, function () {

    console.log("done1");

    waitCallback(2, {}, function () {
        console.log("done2");
    });
});
  • Deferred で
waitDeferred(3).then(function (data) {

    console.log("done1");

    return waitDeferred(2);
}).done(function (data) {
    // 上の then の戻り値の promise へのコールバック
    console.log("done2");
});
  • then, done の方が直感的で読みやすい?
  • 3つとかつなげると違うかも。

同時に非同期処理、両方終わったらなんかする

  • 終わった処理をフラグにして、共通の関数の中で判定
// 終わったかフラグ
var p1End = false,
    p2End = false;

function callback(data) {
    if (p1End && p2End) {
        console.log('p1 & p2 end');
    }
}

var promise1 = $.ajax({
    url: './dummy.json?v=1',
    type: 'GET',
    dataType: 'json',
    success: function () {
        p1End = true;
        callback();
    }
});

var promise2 = $.ajax({
    url: './dummy.json?v=2',
    type: 'GET',
    dataType: 'json',
    success: function () {
        p2End = true;
        callback();
    }
});
  • Deferred
var promise1 = $.ajax({
    url: './dummy.json?v=1',
    type: 'GET',
    dataType: 'json'
});

var promise2 = $.ajax({
    url: './dummy.json?v=2',
    type: 'GET',
    dataType: 'json'
});

$.when(promise1, promise2).done(function () {
    console.log(arguments);
});

これは、確実にDeferred使った方が良さそう

まとめ

  • Deferredを使った方が良いとき
    • 複数処理を並列、順番で走らせる
    • 複数の処理を、状況に応じて組み替えるとき

例えば

  • FBのログインチェック
    • してなかったらログイン
      • ログイン終わったらタイムラインを呼び出す
    • してたらタイムラインを呼び出す

みたいな、ややこしい状態遷移態遷移の時

注意

$('element')
	.css()
	.attr();

普段のjQueryは全部同じインスタンスを返してチェインするが

waitDeferred(3)
	.then(function (data) { return waitDeferred(2); })
	.done(function (data) {});

ここは別のオブジェクトでチェインしている。
then は一個目の wait、done は二個目の wait の戻り値。

@ashikawa
Copy link
Author

ashikawa commented Dec 5, 2012

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment