$.whenはPromise.allと同じなのかを解説

$.whenはPromise.allの完全互換ではありません

結論から書きます。
jQueryの`$.when()`はPromise.allと似ていますが、同じ挙動ではありません。

特に次の2点が大きく違います。

  • 受け取る値の形
  • エラー時の挙動

この違いを知らずに置き換えると、非同期処理の結果が壊れます。
実際、jQueryからES6へ移行する際のトラブルの多くがここで発生します。

まずPromise.allの動作を整理する

Promise.allは複数のPromiseがすべて成功したときに結果をまとめて返します。

Promise.all([
  fetch("/a.json").then(r=>r.json()),
  fetch("/b.json").then(r=>r.json())
]).then(([a,b])=>{
  console.log(a,b);
});

特徴は次の通りです。

  • 全成功でthenに入る
  • 1つでも失敗するとcatch
  • 結果は配列で返る

つまり「すべて成功しないと進まない」構造です。

$.whenの基本動作

同じことをjQueryで書くとこうなります。

$.when(
  $.ajax("/a.json"),
  $.ajax("/b.json")
).done(function(a,b){
  console.log(a,b);
});

ここまでは同じに見えます。
しかしここからが違います。

最大の違い:返り値の構造

Promise.allは配列を返します。

[aData, bData]

一方、`$.when()`は配列ではありません。
各引数が「別の引数」として渡されます。

さらに重要な点があります。
jQuery.ajaxの戻り値は単なるデータではありません。

実際に`a`の中身はこうです。

内容
data
statusText
jqXHR

つまり`$.when()`の引数`a`は「レスポンスそのもの」ではなく、
レスポンス情報のセットです。

したがって、次のコードは誤りです。

$.when($.ajax("/a.json")).done(function(a){
  console.log(a.name);
});

正しくはこうなります。

$.when($.ajax("/a.json")).done(function(a){
  console.log(a[0].name);
});

この違いが最初の落とし穴です。

失敗時の挙動の違い

Promise.allは1つでも失敗するとcatchに入ります。

Promise.all([p1,p2]).catch(()=>console.log("失敗"));

一方`$.when()`はfailに入りますが、
失敗した通信の情報だけが渡されます。

$.when(p1,p2).fail(function(err){
  console.log(err);
});

ここで問題が起きます。

  • どのリクエストが失敗したか分かりにくい
  • 成功した結果が失われる

Promise.allも同様に全体が失敗扱いですが、
Promiseは個別catchやallSettledで回避できます。
jQueryにはこれがありません。

非同期以外も受け取る挙動

`$.when()`にはPromise.allにない特徴があります。

Promiseではない値も受け取れます。

$.when(10, "abc").done(function(a,b){
  console.log(a,b);
});

これは即時実行されます。
Promise.allでは必ずPromiseに包まれます。

この仕様により、コードが予想外に同期実行されることがあります。
jQuery特有のバグの原因です。

単一引数時の挙動

さらに重要な違いです。

Promise.allは常にPromiseを返します。

しかし`$.when()`は引数が1つのとき、
そのDeferredをそのまま返します。

var a = $.when($.ajax("/a"));

この結果、チェーンのタイミングが変わることがあります。
Promiseへの移行時に挙動がズレる典型例です。

なぜこの違いが生まれたのか

理由は設計思想です。

jQuery DeferredはPromise仕様が確立する前に作られました。
そのため「利便性」を優先しています。

  • 複数通信を簡単にまとめる
  • 同期値も扱える
  • 柔軟にコールバックを呼ぶ

一方Promiseは「安全性」と「一貫性」を優先しています。

  • 常に非同期
  • 常に同じ戻り値
  • 状態は変更不可

つまり`$.when()`は便利ツール、
Promise.allは仕様です。

移行時の注意点

jQueryからPromiseへ置き換える際、最も危険なのはこの書き方です。

$.when($.ajax("/a"), $.ajax("/b"))
  .done(function(a,b){ ... });

これを単純にPromise.allへ置き換えると、
受け取るデータ構造が変わり動かなくなります。

正しい置換は次です。

Promise.all([
  fetch("/a").then(r=>r.json()),
  fetch("/b").then(r=>r.json())
]).then(([a,b])=>{ ... });

ここを修正しないと、本番でのみエラーが出るケースがあります。

まとめ

`$.when()`とPromise.allは目的は同じです。
「複数の非同期完了を待つ」ことです。

しかし設計はまったく異なります。

  • $.when … コールバック中心・柔軟
  • Promise.all … 仕様中心・厳密

見た目が似ているため混同されやすいですが、
移行時に最も事故が起きやすいポイントです。

jQueryコードを現代のJavaScriptへ移すときは、
まずこの違いを確認することが安全な第一歩になります。