jQueryの据え置きと約束-.then()と.done()


473

私はjQueryの遅延とプロミスについて読んでいますが、コールバックの成功に.then().done()を使用することの違いがわかりません。私が知っているエリックHyndsがいることを言及.done()して.success()同じ機能にマッピングするが、私はそう推測している.then()すべてのコールバックがすべて成功した操作の完了時に呼び出されるよう。

誰かが正しい使い方を教えてくれますか?


15
2016年6月にリリースされたJQuery 3.0は、Promises / A +およびES2015 Promises仕様に準拠した最初のバージョンであることを、すべての人が覚えておいてください。それ以前の実装は、約束が果たすべきものと非互換でした。
Flimm

いつ使用するかについての改善された推奨事項で回答を更新しました。
Robert Siemer

回答:


577

にアタッチされたコールバックdone()は、遅延オブジェクトが解決されると発生します。にアタッチされたコールバックfail()は、遅延オブジェクトが拒否されたときに発生します。

jQuery 1.8より前then()は、単なる構文上の砂糖でした。

promise.then( doneCallback, failCallback )
// was equivalent to
promise.done( doneCallback ).fail( failCallback )

1.8以降then()、はのエイリアスでpipe()あり、新しいpromiseを返します。の詳細については、こちらを参照してくださいpipe()

success()およびへの呼び出しによって返されるオブジェクトでerror()のみ使用できます。これらは、それぞれの単純なエイリアスです。jqXHRajax()done()fail()

jqXHR.done === jqXHR.success
jqXHR.fail === jqXHR.error

また、done()単一のコールバックに限定されず、非関数を除外します(1.8.1で修正されるバージョン1.8の文字列にはバグがあります)。

// this will add fn1 to 7 to the deferred's internal callback list
// (true, 56 and "omg" will be ignored)
promise.done( fn1, fn2, true, [ fn3, [ fn4, 56, fn5 ], "omg", fn6 ], fn7 );

同じことが当てはまりますfail()


8
then新しい約束を返すことは私が欠けていた重要なことでした。のようなチェーン$.get(....).done(function(data1) { return $.get(...) }).done(function(data2) { ... })data2undefined で失敗する理由を理解できませんでした。私が変更doneしたときはthen、元のプロミスにハンドラーを追加するのではなく、プロミスを一緒にパイプ処理したかったので、うまくいきました。
wrschneider 2015年

5
jQuery 3.0は、Promises / A +およびES2015仕様に準拠した最初のバージョンです。
Flimm

4
なぜ私は他のものを使うのかまだ分かりません。ajax呼び出しを行い、その呼び出しが完全に完了する(つまり、サーバーから応答が返される)まで待機する必要がある場合、別のajax呼び出しを呼び出す前に、doneまたはを使用しますかthen?どうして?
CodingYoshi、

@CodingYoshi 私の答えをチェックして最終的にその質問に答えてください(を使用.then())。
Robert Siemer

413

返される結果が処理される方法にも違いがあります(これはチェーンと呼ばれ、呼び出しdoneチェーンをthen生成する間はチェーンしません)。

promise.then(function (x) { // Suppose promise returns "abc"
    console.log(x);
    return 123;
}).then(function (x){
    console.log(x);
}).then(function (x){
    console.log(x)
})

次の結果がログに記録されます。

abc
123
undefined

ながら

promise.done(function (x) { // Suppose promise returns "abc"
    console.log(x);
    return 123;
}).done(function (x){
    console.log(x);
}).done(function (x){
    console.log(x)
})

以下を取得します:

abc
abc
abc

----------更新:

ところで アトミックタイプの値ではなくPromiseを返すと、外側のプロミスは内側のプロミスが解決するまで待機します。

promise.then(function (x) { // Suppose promise returns "abc"
    console.log(x);
    return $http.get('/some/data').then(function (result) {
        console.log(result); // suppose result === "xyz"
        return result;
    });
}).then(function (result){
    console.log(result); // result === xyz
}).then(function (und){
    console.log(und) // und === undefined, because of absence of return statement in above then
})

このようにして、次のような並列または順次の非同期操作を構成することが非常に簡単になります。

// Parallel http requests
promise.then(function (x) { // Suppose promise returns "abc"
    console.log(x);

    var promise1 = $http.get('/some/data?value=xyz').then(function (result) {
        console.log(result); // suppose result === "xyz"
        return result;
    });

    var promise2 = $http.get('/some/data?value=uvm').then(function (result) {
        console.log(result); // suppose result === "uvm"
        return result;
    });

    return promise1.then(function (result1) {
        return promise2.then(function (result2) {
           return { result1: result1, result2: result2; }
        });
    });
}).then(function (result){
    console.log(result); // result === { result1: 'xyz', result2: 'uvm' }
}).then(function (und){
    console.log(und) // und === undefined, because of absence of return statement in above then
})

上記のコードは2つのhttpリクエストを並行して発行するため、リクエストはより早く完了しますが、以下のHTTPリクエストは順次実行されるため、サーバーの負荷が軽減されます

// Sequential http requests
promise.then(function (x) { // Suppose promise returns "abc"
    console.log(x);

    return $http.get('/some/data?value=xyz').then(function (result1) {
        console.log(result1); // suppose result1 === "xyz"
        return $http.get('/some/data?value=uvm').then(function (result2) {
            console.log(result2); // suppose result2 === "uvm"
            return { result1: result1, result2: result2; };
        });
    });
}).then(function (result){
    console.log(result); // result === { result1: 'xyz', result2: 'uvm' }
}).then(function (und){
    console.log(und) // und === undefined, because of absence of return statement in above then
})

121
done結果をthen変更する結果に対して何もしないという概念の+1 。他のイモが見逃した巨大なポイント。
シャニマール2013年

9
これはおそらく、これは動作のために、に適用されるのjQueryのバージョンを言及する価値がthen1.8に変更
bradley.ayers

4
+1ポイントにまっすぐ。私が作成した実行可能な例を、誰もが混在してどのようなチェーン見たい場合donethen呼び出しの結果インチ
マイケルKropat

7
上記の例は、「done」が最初に作成された元のpromiseオブジェクトで機能するが、「then」は新しいpromiseを返すことも強調しています。
Pulak Kanti Bhattacharyya 2014

2
これはjQuery 1.8以降に適用されます。古いバージョンはdone例のように動作します。1.8以上の動作を実現するには、1.8より前のバージョンに変更thenpipeてくださいthen
David Harkness 2014

57

.done() コールバックは1つだけあり、それは成功コールバックです

.then() 成功と失敗の両方のコールバックがあります

.fail() 失敗したコールバックは1つだけです

だから、あなたがしなければならないことはあなた次第です...それが成功するか失敗するかどうか気にしますか?


18
'then'がコールチェーンを生成するとは言い切れません。Lu4の答えを見てください。
オリゴフレン2013年

あなたの答えは2011年からです...最近では、戻り値はとはthen()大きく異なりdone()ます。同様にthen()、多くの場合、/覚えて知っている主なものよりも詳細は唯一のあなたのポイントコールバックの成功ではなく、あると呼ばれています。(jQuery 3.0より前の状態は言えません。)
Robert Siemer

14

deferred.done()

Deferredが解決されたときにのみ呼び出されるハンドラーを追加します。呼び出すコールバックを複数追加できます。

var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).done(doneCallback);

function doneCallback(result) {
    console.log('Result 1 ' + result);
}

上記のように書くこともできます

function ajaxCall() {
    var url = 'http://jsonplaceholder.typicode.com/posts/1';
    return $.ajax(url);
}

$.when(ajaxCall()).then(doneCallback, failCallback);

deferred.then()

Deferredが解決、拒否、または進行中のときに呼び出されるハンドラーを追加します。

var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).then(doneCallback, failCallback);

function doneCallback(result) {
    console.log('Result ' + result);
}

function failCallback(result) {
    console.log('Result ' + result);
}

投稿ではthenfailコールバックが提供されていない場合の動作、つまりfailケースをまったくキャプチャしていない場合の動作が明確になっていません
BM

失敗の場合は、プログラムの最上位でキャッチできる例外が発生します。JavaScriptコンソールで例外を確認することもできます。
David Spector

10

jQueryのDeferredがPromiseの実装であることを意図している限り(そしてjQuery3.0は実際にそれらを仕様に組み込もうとしています)、実際にはかなり重大な違いがあります。

done / thenの主な違いは、

  • .done() 何をするか、何を返すかに関係なく、常に、最初と同じPromise /ラップされた値を返します。
  • .then() 常に新しいPromiseが返され、渡された関数が返したものに基づいてそのPromiseが何であるかを制御する責任があります。

jQueryからネイティブES2015 Promisesに変換され.done()ます。これは、Promiseチェーン内の関数の周りに「タップ」構造を実装するようなもので、チェーンが「解決」状態の場合、値を関数に渡します。 。しかし、その関数の結果はチェーン自体には影響しません。

const doneWrap = fn => x => { fn(x); return x };

Promise.resolve(5)
       .then(doneWrap( x => x + 1))
       .then(doneWrap(console.log.bind(console)));

$.Deferred().resolve(5)
            .done(x => x + 1)
            .done(console.log.bind(console));

それらは両方とも6ではなく5を記録します。

.thenではなく、doneとdoneWrapを使用してログを記録したことに注意してください。これは、console.log関数が実際には何も返さないためです。そして、何も返さない関数を.thenに渡すとどうなりますか?

Promise.resolve(5)
       .then(doneWrap( x => x + 1))
       .then(console.log.bind(console))
       .then(console.log.bind(console));

それはログに記録します:

5

未定義

どうした?.thenを使用して何も返さない関数を渡した場合、その暗黙の結果は「未定義」でした...もちろん、Promise [undefined]を次のthenメソッドに返し、未定義のログを記録しました。したがって、私たちが始めた当初の価値は基本的に失われました。

.then()基本的には、関数構成の形式です。各ステップの結果は、次のステップの関数の引数として使用されます。そのため、.doneは「タップ」と考えるのが最適です->実際にはコンポジションの一部ではなく、特定のステップで値を調べ、その値で関数を実行するだけで、実際には変更しません。なんとなく構成。

これはかなり根本的な違いです。ネイティブのPromiseに.doneメソッドが実装されていないのには、おそらくそれなりの理由があります。.failメソッドが存在しない理由を説明する必要はありません。これはさらに複雑なためです(つまり、.fail / .catchは.done / .thenのミラーではないため、裸の値を返す.catchの関数ではありません。 「滞在」は、.thenに渡されたものと同様に拒否され、解決します!)


6

then()常に、どのような場合でも呼び出されることを意味します。ただし、渡されるパラメーターは、jQueryのバージョンによって異なります。

jQuery 1.8より前でthen()は、はに等しくなりdone().fail()ます。そして、すべてのコールバック関数は同じパラメーターを共有します。

ただし、jQuery 1.8以降でthen()は、新しいpromiseを返します。値が返された場合は、次のコールバック関数に渡されます。

次の例を見てみましょう。

var defer = jQuery.Deferred();

defer.done(function(a, b){
            return a + b;
}).done(function( result ) {
            console.log("result = " + result);
}).then(function( a, b ) {
            return a + b;
}).done(function( result ) {
            console.log("result = " + result);
}).then(function( a, b ) {
            return a + b;
}).done(function( result ) {
            console.log("result = " + result);
});

defer.resolve( 3, 4 );

jQuery 1.8以前は、答えは

result = 3
result = 3
result = 3

すべてresultが3をとります。then()関数は常に同じ遅延オブジェクトを次の関数に渡します。

しかし、jQuery 1.8以降、結果は次のようになります。

result = 3
result = 7
result = NaN

最初のthen()関数は新しいpromiseを返し、値7(これは渡される唯一のパラメーターです)が次のdone()に渡されるため、2番目のdone()書き込みresult = 7です。2番目then()はの値として7をa取りundefined、の値として取るbので、2番目then()はパラメーターNaNを使用して新しいプロミスを返し、最後done()は結果としてNaNを出力します。


「then()は常に、どのような場合でも呼び出されることを意味します」-trueではありません。then()は、Promise内でエラーが発生した場合には呼び出されません。
David Spector

jQuery.Deferred()が複数の値を受け取ることができる興味深い側面です.then()。それは最初の値に適切に渡されます。— 少し奇妙ですが...次のように.then()はできません。(選択されたインターフェース経由returnで返すことができる値は1つだけです。)JavascriptのネイティブPromiseはそれを行いません。(正直に言うと、より一貫性があります。)
Robert Siemer


2

使用のみ .then()

これらはの欠点です .done()

  • 連鎖できません
  • ブロックresolve()呼び出し(すべての.done()ハンドラーは同期的に実行されます)
  • resolve()登録された.done()ハンドラーから例外を受け取る可能性があります(!)
  • .done()延期されたハーフキルの例外:
    • それ以上の.done()ハンドラは黙ってスキップされます

私はそれを一時的に考え.then(oneArgOnly)、常に必要です.catch():例外は黙って無視されますが、それはもはや真実ではないので、unhandledrejectionイベントが未処理のログに記録します.then()(デフォルトとして)コンソール上の例外を。とてもリーズナブル!使用する理由は.done()まったく残っていません。

証明

次のコードスニペットはそれを明らかにします:

  • すべての.done()ハンドラは、次の時点で同期的に呼び出されますresolve()
    • 1、3、5、7として記録
    • スクリプトが下に落ちる前に記録されます
  • .done()影響を与えるresolve()呼び出し元の 例外
    • キャッチアラウンドで記録 resolve()
  • 例外はさらなる.done()解決からの約束を破ります
    • 8と10は記録されません!
  • .then() これらの問題はありません
    • スレッドがアイドルになった後に2、4、6、9、11として記録
    • (スニペット環境は無いunhandledrejectionようです)

ところで、からの例外を.done()適切にキャッチできません:の同期パターンの.done()ため、エラーは.resolve()(ライブラリコードの可能性があります!)の時点で、または.done()遅延オブジェクトが既に解決されている場合は原因をアタッチする呼び出しでスローされます。

console.log('Start of script.');
let deferred = $.Deferred();
// deferred.resolve('Redemption.');
deferred.fail(() => console.log('fail()'));
deferred.catch(()=> console.log('catch()'));
deferred.done(() => console.log('1-done()'));
deferred.then(() => console.log('2-then()'));
deferred.done(() => console.log('3-done()'));
deferred.then(() =>{console.log('4-then()-throw');
    throw 'thrown from 4-then()';});
deferred.done(() => console.log('5-done()'));
deferred.then(() => console.log('6-then()'));
deferred.done(() =>{console.log('7-done()-throw');
    throw 'thrown from 7-done()';});
deferred.done(() => console.log('8-done()'));
deferred.then(() => console.log('9-then()'));

console.log('Resolving.');
try {
    deferred.resolve('Solution.');
} catch(e) {
    console.log(`Caught exception from handler
        in resolve():`, e);
}
deferred.done(() => console.log('10-done()'));
deferred.then(() => console.log('11-then()'));
console.log('End of script.');
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh"
crossorigin="anonymous"
></script>


いくつかのこと:1)done以前の完了に例外がある場合は実行されないというあなたの言っていることがわかります。しかし、なぜそれが黙って無視されるのでしょうか、つまり例外が発生したので、なぜ黙っていますか。2)DeferredオブジェクトのAPIが非常に不十分であるため、オブジェクトを軽んじています。複雑すぎてわかりにくい。ここでのコードは、あなたの要点を証明するのにも役立ちませんし、証明しようとしていることに対して、必要以上に複雑すぎます。3)doneインデックス2、4、6が2番目の前に実行されるのはthenなぜですか?
CodingYoshi

私の悪い、ええ、間違いなく投票に値する。例外に関するコメントについては、通常は例外が機能する方法です。一度発生すると、その後のコードは実行されません。さらに、jQueryのドキュメントには、遅延オブジェクトが解決された場合にのみ実行されると記載されています。
CodingYoshi

@CodingYoshiここでは状況が異なります。私は解決済みの約束/据え置きについてのみ話していました。サクセスハンドラーの残りの部分が呼び出されないことは不平を言っていませんが、それは正常です。しかし、私は、成功した約束について完全に異なるサクセスハンドラーが呼び出されない理由はないと思います。.then()例外が発生するかどうかにかかわらず、それらのハンドラで例外がすべて呼び出されます。しかし、追加/残りの.done()休憩。
Robert Siemer

@CodingYoshi言わせてもらえれば、答えが大幅に改善されました。コードとテキスト。
Robert Siemer

1

jQuery 3.0には、予期しない動作を簡単に引き起こす可能性があり、以前の回答では言及されていない重要な違いがもう1つあります。

次のコードを検討してください。

let d = $.Deferred();
d.done(() => console.log('then'));
d.resolve();
console.log('now');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>

これは出力します:

then
now

さて、交換するdone()ことにより、then()非常に同じスニペット:

var d = $.Deferred();
d.then(() => console.log('then'));
d.resolve();
console.log('now');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>

出力は今です:

now
then

したがって、即時に解決される遅延の場合、渡される関数done()は常に同期的に呼び出されますが、渡される引数はすべてthen()非同期で呼び出されます。

これは、アップグレードガイドで言及されているように、両方のコールバックが同期的に呼び出される以前のjQueryバージョンとは異なります

Promises / A +コンプライアンスに必要なもう1つの動作変更は、Deferred .then()コールバックが常に非同期で呼び出されることです。以前は、解決または拒否された.then()コールバックがDeferredに追加された場合、コールバックはすぐに同期的に実行されました。


-5

.done()Promiseチェーンを終了し、他に何も追加できないことを確認します。これは、jQuery promise実装が未処理の例外をスローする可能性があることを意味し.fail()ます。

実際には、promiseにさらにステップを追加する予定がない場合は、を使用する必要があります.done()。詳細については、約束をする必要がある理由をご覧ください


6
注意!この回答は、いくつかのpromise実装には当てはまり.done()ますが、終了の役割がないjQueryには当てはまりません。ドキュメントには、「deferred.done()は遅延オブジェクトを返すため、追加の.done()メソッドを含め、遅延オブジェクトの他のメソッドをこのオブジェクトにチェーンすることができます」と記載されています。.fail()言及されていませんが、はい、それは連鎖する可能性もあります。
Roamer-1888 2014年

1
私の悪いことに、jQueryをチェックしませんでした
gleb bahmutov '30 / 10/30

1
@glebbahmutov-他の人が混乱しないように、この回答を削除する必要がありますか?丁寧な提案:)
Andrey

2
答えを削除しないでください。これは人々が誤解を解消するのにも役立ちます。
Melissa、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.