Skip to content

Latest commit

 

History

History
142 lines (104 loc) · 6.31 KB

deferred-promise.adoc

File metadata and controls

142 lines (104 loc) · 6.31 KB

DeferredとPromise

このセクションではDeferredとPromiseの関係について簡潔に学んでいきます。

Deferredとは何か

Deferredという単語はPromiseと同じコンテキストで聞いたことがあるかもしれません。 有名な所だと jQuery.DeferredJSDeferred 等があげられるでしょう。

DeferredはPromiseと違い、共通の仕様があるわけではなく、各ライブラリがそのような目的の実装をそう呼んでいます。

今回は jQuery.Deferred のようなDeferredの実装を中心にして話を進めます。

DeferredとPromiseの関係

DeferredとPromiseの関係を簡単に書くと以下のようになります。

  • Deferred は Promiseを持っている

  • Deferred は Promiseの状態を操作する特権的なメソッドを持っている

DeferredとPromise
Figure 1. DeferredとPromise

この図を見ると分かりますが、DeferredとPromiseは比べるような関係ではなく、 DeferredがPromiseを内蔵しているような関係になっていることが分かります。

Note
jQuery.Deferredの構造を簡略化したものです。Promiseを使わないDeferredの実装もあります。

図だけだと分かりにくいので、実際にPromiseを使ってDeferredクラスを実装してみましょう。

Note

ECMAScript 2015ではクラスを定義する class 構文が導入されています。 class 構文を使ったクラス定義については、次のページを参照してください。

Deferred top on Promise

Promiseの上にDeferredクラスを実装した例です。

deferred.js
link:embed/embed-deferred.js[role=include]

以前Promiseを使って実装したfetchURLをこのDeferredで実装しなおしてみます。

xhr-deferred.js
link:embed/embed-xhr-deferred.js[role=include]
// 実行例
const URL = "https://httpbin.org/get";
fetchURL(URL).then(function onFulfilled(value){
    console.log(value);
}).catch(console.error.bind(console));

Promiseの状態を操作する特権的なメソッドというのは、 promiseオブジェクトの状態をresolve、rejectすることができるメソッドで、 通常のPromiseだとコンストラクタで渡した関数の中でしか操作することができません。

通常のPromiseで実装したものと見比べていきたいと思います。

xhr-promise.js
link:../Ch1_WhatsPromises/embed/embed-xhr-promise.js[role=include]
// 実行例
const URL = "https://httpbin.org/get";
fetchURL(URL).then(function onFulfilled(value){
    console.log(value);
}).catch(console.error.bind(console));

2つの fetchURL を見比べて見ると以下のような違いがあることが分かります。

  • Deferred の場合は全体がPromiseで囲まれていない

    • 関数で囲んでないため、1段ネストが減っている

    • Promiseコンストラクタの中で処理が行われていないため、自動的に例外をキャッチしない

逆に以下の部分は同じことをやっています。

  • 全体的な処理の流れ

    • resolvereject を呼ぶタイミング

  • 関数はpromiseオブジェクトを返す

このDeferredはPromiseを持っているため、大きな流れは同じですが、 Deferredには特権的なメソッドを持っていることや自分で流れを制御する裁量が大きいことが分かります。

たとえば、Promiseの場合はコンストラクタの中に処理を書くことが通例なので、 resolvereject を呼ぶタイミングが大体みて分かります。

new Promise((resolve, reject) => {
    // この中に解決する処理を書く
});

一方Deferredの場合は、関数的なまとまりはないのでdeferredオブジェクトを作ったところから、 任意のタイミングで resolvereject を呼ぶ感じになります。

const deferred = new Deferred();

// どこかのタイミングでdeferred.resolve or deferred.rejectを呼ぶ

このように小さなDeferredの実装ですがPromiseとの違いが出ていることが分かります。

これは、Promiseが値を抽象化したオブジェクトなのに対して、 Deferredはまだ処理が終わってないという状態や操作を抽象化したオブジェクトである違いがでているのかもしれません。

言い換えると、 Promiseはこの値は将来的に正常な値(Fulfilled)か異常な値(Rejected)が入るというものを予約したオブジェクトなのに対して、 Deferredはまだ処理が終わってないということを表すオブジェクトで、 処理が終わった時の結果を取得する機構(Promise)に加えて処理を進める機構をもったものといえるかもしれません。

より詳しくDeferredについて知りたい人は以下を参照するといいでしょう。

Note

DeferredはPythonの Twisted というフレームワークが最初に定義した概念です。 JavaScriptへは MochiKit.Asyncdojo/Deferred 等のライブラリがその概念を持ってきたと言われています。