素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: twitter: @javascripter にどうぞ。

Promises/Deferredsを使うとき

現状ではPromiseを生成する手段がライブラリによって異なるので、統一されたインターフェースを作ってそれを使うようにするとライブラリを自由に変更できて非常に便利である。

今のところ自分が考えた中で一番最大公約数的かつ便利な関数はこんなかんじ。

/*
A unified interface for Promises
   
The advantages of this interface are as follows:
- it does not require a Deferred concept so it can avoid the naming argument (fulfill or resolve)
- it clarifies that fulfill and reject functions can be used without Function#bind
- it allows extensions for both class-based and object-based promises
- extensions are local and can be done safely
- promise itself will be passed as `this`, and use of `this.then` inside will be safe for both extend(mixins, promise) and mixins.__proto__ = promise

promise function can pass `progress` as 3rd argument but arguments after 3rd are not expected as Promises are just try/catch semantics for the async
*/


// pass a setup function that receives fulfill and reject as arguments instead of making a deferred

var p = promise(function (fulfill, reject) {
  var tid = setTimeout(function () { fulfill(); }, 1000);
  return {
    cancel: function () {
      clearTimeout(tid);
      reject();
    },
    try: function (fn) {
      this.then(fn, null);
    }
  }; // allow for extensions for mix-ins here instead of extending Promise#prototype
});

p.try(function () {
  // will not be called as it will be cancelled!
}).cancel();

promise関数にsetup関数を渡すと引数としてresolve, reject関数が渡されて、setupのthisにはpromiseオブジェクトが入って、setupの返り値に渡すオブジェクトがpromiseの拡張に使われる。 promise関数の実装はjQueryだと

function promise(setup) {
  var d = jQuery.Deferred();
  var p = d.promise();
  return jQuery.extend(p, setup.call(p, d.resolve, d.reject));
}

になる気がして、まあだいたいどのライブラリ使ってても問題なくすぐにアダプターを書ける