jQuery: Deferred Object

使用 Deferred Object 處理非同步狀況。

建立 Deferred 物件

Deferred 物件是由 $.Deferred() 建立的。

var $d = $.Deferred();

Deferred 物件的狀態

Deferred 物件的狀態有三種:resolved、rejected 和 pending。

備註:若回傳非 Promise 或 Deferred 物件,則會視為 resolved 而進入 done()

狀態檢測

可使用 $d.state() 確認 Deferred 物件的狀態。

如下例,由於 getUserProfile() 會在三秒後使用 resolve 更新 $d 的狀態為 resolved,因此在 done() 中檢測狀態可得到 resolved。

程式碼。

var $d = $.Deferred();

function getUserProfile() {
  setTimeout(function() {
    $d.resolve({
      id: '123456789',
      name: 'Peter Wang',
      email: 'sample@test.com',
    });
  }, 3000);

  return $d.promise();
}

getUserProfile().done(function(res) {
  console.log($d.state()); // resolved
});

Demo。

jQuery: Deferred Object

resolve

更改 Deferred 物件狀態的 method 有 resolve()reject()

若成功取得資訊,則使用 resolve() 更新狀態為 resolved,並可回傳資訊,然後在 done() 接到結果。如下例,三秒後顯示訊息「Hi, Peter Wang」。

程式碼。

function getUserProfile() {
  var $d = $.Deferred();

  setTimeout(function() {
    $d.resolve({
      id: '123456789',
      name: 'Peter Wang',
      email: 'sample@test.com',
    });
  }, 3000);

  return $d.promise();
}

getUserProfile().done(function(res) {
  console.log('Hi, ' + res.name); // Hi, Peter Wang
});

Demo。

jQuery: Deferred Object

reject

若取得資訊失敗,則使用 reject() 更新狀態為 rejected,並可回傳錯誤訊息,然後在 fail() 接到結果。如下例,三秒後顯示訊息「Get User Profile Error :(」。

程式碼。

function getUserProfile() {
  var $d = $.Deferred();

  setTimeout(function() {
    $d.reject({
      msg: 'Get User Profile Error :(',
    });
  }, 3000);

  return $d.promise();
}

getUserProfile().fail(function(res) {
  console.log(res.msg); // Get User Profile Error :(
});

Demo。

jQuery: Deferred Object

nofity 與 progress

當 Deferred 物件尚未設定為 resolved 或 rejected 狀態,表示仍在進行中,則可使用 notify() 回報目前的執行狀況。如下例,每 0.5 秒印出目前 counter 的數字,直到 3 停止。

程式碼。

function reportProgress() {
  var $d = $.Deferred();
  var count = 0;
  var intervalId = setInterval(function() {
    $d.notify(count++);
    count > 3 && clearInterval(intervalId);
  }, 500);

  return $d.promise();
}

var promise = reportProgress();

promise.progress(function(prog) {
  console.log(prog);
});

Demo。

then

done()fail()progress() 的 callback 合併至 then()

getUserProfile().then(
  function(res) {
    console.log('done');
  },
  function(res) {
    console.log('fail');
  },
  function() {
    console.log('still in progress...');
  },
);

promise

使用 $d.promise() 來回傳 Deferred 物件狀態。如下所示,回傳 $d 為 resolved 狀態。

var $d = $.Deferred();

function getUserProfile() {
  $d.resolve({
    // ...
  });

  return $d.promise();
}

pipe

$d.pipe 屬於工具型的 method,用來串聯(and / or)的 Deferred 物件。

如下例,將 name 這個欄位後面加上「++」字串。

var $d = $.Deferred();
var filtered = $d.pipe(function(value) {
  return $.extend({}, value, { name: value.name + '++' });
});

function getUserProfile() {
  setTimeout(function() {
    $d.resolve({
      id: '123456789',
      name: 'Peter Wang',
      email: 'sample@test.com',
    });
  }, 500);

  return $d.promise();
}

getUserProfile();

filtered.done(function(res) {
  console.log(res);
});

jQuery: Deferred Object

when

使用 $.when() 來合併多個 Deferred 物件,任一回傳 rejected 就會進入 fail。如下例,等待 f1、f2 和 f3 執行完成後,印出執行結果。

function f1() {
  var $d = $.Deferred();

  setTimeout(function() {
    $d.resolve('f1 done');
  }, 1000);

  return $d.promise();
}

function f2() {
  var $d = $.Deferred();

  setTimeout(function() {
    $d.resolve('f2 done');
  }, 3000);

  return $d.promise();
}

function f3() {
  var $d = $.Deferred();

  setTimeout(function() {
    $d.resolve('f3 done');
  }, 2000);

  return $d.promise();
}

$.when(f1(), f2(), f3()).done(function(res1, res2, res3) {
  console.log(res1);
  console.log(res2);
  console.log(res3);
});

jQuery: Deferred Object

任一回傳 rejected 就會進入 fail。如下例, f2 回傳狀態 rejected(已拒絕)。

function f2() {
  var $d = $.Deferred();

  setTimeout(function() {
    $d.reject('f2 done');
  }, 3000);

  return $d.promise();
}

$.when(f1(), f2(), f3()).fail(function(res1, res2, res3) {
  console.log(res1);
  console.log(res2);
  console.log(res3);
});

jQuery: Deferred Object

Callback

done

當 Deferred 物件狀態為 resolved 時的處理 handler,可加入多個 callback。

程式碼。

function getUserProfile() {
  var $d = $.Deferred();

  setTimeout(function() {
    $d.resolve({
      id: '123456789',
      name: 'Peter Wang',
      email: 'sample@test.com',
    });
  }, 3000);

  return $d.promise();
}

function f1() {
  console.log('f1 done');
}

function f2() {
  console.log('f2 done');
}

function f3(res) {
  console.log('f3 done with ' + res.name);
}

getUserProfile()
  .done(f1, f2)
  .done(function(res) {
    f3(res);
  });

Demo。

jQuery: Deferred Object

fail

當 Deferred 物件狀態為 rejected 時的處理 handler。

always

無論 Deferred 物件狀態為 resolved 或 rejected,都會執行這個 handler。

程式碼。

getUserProfile()
  .done(function(res) {
    console.log('Get User Profile Success!');
  })
  .fail(function(res) {
    console.log('Get User Profile Error :(');
  })
  .always(function() {
    console.log('Always execute this line!');
  });

Demo。

成功。

jQuery: Deferred Object

失敗。

jQuery: Deferred Object

相關閱讀


jQuery promise deferred