- jQueryのDeferredはPromiseの「古い版」ではありません
- Promiseとは何か(前提整理)
- jQuery Deferredとは何か
- なぜこの違いが重要なのか
- then / done / fail の違い
- jQuery 1.x〜2.x時代の背景
- Promise互換になったのはjQuery3以降
- 注意点:DeferredをPromiseだと思って使うと壊れる
- ではどう扱うべきか
- まとめ
jQueryのDeferredはPromiseの「古い版」ではありません
よく「jQueryのDeferredはPromiseの前身」と説明されます。
半分は正しく、半分は誤解です。
jQuery DeferredはPromiseに似ていますが、同じものではありません。
そしてこの違いを理解しないままコードを書くと、非同期処理のバグに遭遇します。
結論から言うと、
- Promise … 状態を監視する仕組み
- Deferred … 状態を操作できてしまう仕組み
ここが最も重要な違いです。
Promiseとは何か(前提整理)
Promiseは「未来に値が決まる」ことを表すオブジェクトです。
通信やタイマーなどの非同期処理の結果を扱うために使います。
fetch("/api/user") .then(res => res.json()) .then(user => console.log(user));
このとき開発者は「結果を受け取る側」だけを書きます。
Promiseの状態(成功・失敗)を変更することはできません。
つまりPromiseは「結果の通知専用オブジェクト」です。
Promiseの特徴:
- 成功(resolve)は1回だけ
- 失敗(reject)は1回だけ
- 外部から状態変更不可
- チェーン可能
ここが安全性を生みます。
jQuery Deferredとは何か
Deferredは、Promiseのように見える別の仕組みです。
var d = $.Deferred(); setTimeout(function(){ d.resolve("OK"); },1000); d.done(function(result){ console.log(result); });
一見Promiseと同じです。
しかし決定的な違いがあります。
resolveを外部から呼べます。
つまり「非同期の結果」を外から変更できます。
Promiseではこれは禁止されています。
なぜこの違いが重要なのか
Deferredは便利ですが危険です。
理由は「状態が壊れる」可能性があるからです。
例えば次のコードです。
function getUser(){ var d = $.Deferred(); setTimeout(function(){ d.resolve("userA"); },1000); return d; } var req = getUser(); req.done(function(u){ console.log("1:", u); }); req.resolve("userB");
この場合、通信結果とは無関係にuserBが出力されます。
呼び出し側が処理結果を上書きできてしまいます。
Promiseでは起こりません。
Promiseは外部から状態変更できない設計だからです。
これがDeferredの最大の問題です。
then / done / fail の違い
DeferredとPromiseはメソッドも微妙に違います。
| メソッド | Deferred | Promise |
| then | あり | あり |
| done | あり | なし |
| fail | あり | なし |
| catch | なし | あり |
特に重要なのは`done`です。
`done`はエラーを捕捉しません。
$.ajax("/api") .done(function(){ throw new Error("失敗"); });
この例では例外がチェーンに流れません。
デバッグが難しくなります。
Promiseなら`catch`に流れます。
fetch("/api") .then(()=>{ throw new Error(); }) .catch(()=> console.log("捕捉"));
この挙動差は、実務でトラブルの原因になります。
jQuery 1.x〜2.x時代の背景
なぜこのような仕組みになったのでしょうか。
理由は歴史です。
Deferredが作られた当時は、まだPromise仕様(Promise/A+)が確立していませんでした。
非同期処理の標準設計が存在しなかったのです。
そのためjQueryは独自設計で非同期管理を提供しました。
つまりDeferredは「間違った実装」ではありません。
標準が存在しない時代の解決策です。
Promise互換になったのはjQuery3以降
jQuery 3以降、DeferredはPromiseに近づきました。
しかし完全互換ではありません。
特に注意が必要なのは以下です。
- 例外の伝播
- 同期resolve時の挙動
- thenの戻り値
古いjQueryコードをES6 Promiseと混在させると、不思議なバグが発生します。
注意点:DeferredをPromiseだと思って使うと壊れる
典型的な問題です。
return $.ajax("/api").then(res => res.data);
これをPromiseだと思ってasync/awaitで使うと、
例外処理が期待通り動かないケースがあります。
つまり、
Deferred ≠ Promise
Promise風のオブジェクト
です。
ではどう扱うべきか
既存コードでDeferredがある場合は、Promiseに変換するのが安全です。
function toPromise(jqXHR){ return new Promise(function(resolve,reject){ jqXHR.done(resolve); jqXHR.fail(reject); }); }
これによりES6の非同期管理に統一できます。
まとめ
jQuery DeferredとPromiseは似ていますが設計思想が違います。
Deferredは「操作可能な非同期状態」、
Promiseは「通知専用の非同期状態」です。
jQueryの非同期処理が難しく感じる理由の多くは、この違いにあります。
そしてasync/await時代に移行する際、最もつまずきやすいポイントもここです。
Deferredは過去の遺産ではなく、
JavaScriptの非同期設計が進化してきた歴史そのものと言えます。