web-dev-qa-db-ja.com

解決済みの約束を返す

Promiseを返す多くの非同期関数を持つ既存のプロジェクトがあります。非同期関数が同期的に完了し、可能であればこのコードをより短く/より良くしたい場合があるように、キャッシュを追加しています。

        return $.Deferred(function(def) { def.resolve(); }).promise();

たとえば、次のようなほとんどのAJAXリクエストを処理するData Serviceクラスがあります。

function DataService() {

    var self = this;

    self.makeRequest = function(functionName, data) {
        return $.Deferred(function(def) {

            var jsonData = JSON.stringify(data);

            $.ajax({
                type: "POST",
                url: "WebService.asmx/" + functionName,
                data: jsonData,
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                error: function(xhr, status, error) {
                    var ex;
                    try {
                        ex = eval("(" + xhr.responseText + ")");
                        ex.message = ex.Message;
                        ex.Message = undefined;
                    } catch (ex2) {
                        ex = { message: "Invalid Response From Server:\r\n" + xhr.responseText };
                    }
                    if (ex.message == "LoginRequired") {
                        app.viewModels.main.loginRequired(true);
                    }
                    else {
                        app.showError(ex.message);
                    }
                    def.reject(ex.message);
                }
            });
        }).promise();
    }
}

次に、現在常にmakeRequestを呼び出す別のクラスの関数があります。

self.deleteMe = function()
{
   return app.dataservice.makeRequest('deleteItem');
}

DeleteMe関数を更新して、常にmakeRequestを呼び出すのではなく、代わりに同期作業を行ってから戻るようにします。ただし、呼び出されたものはそれを期待するため、Promiseを返す必要がありますが、「すでに完了/解決済みのPromise」である必要があります。現在、私はそれを行うために上記のコードの最初のセットを使用しています。もっと良い方法があるに違いないようです。

43
eselk

@Eselk、

私の経験では、$.Deferred(function(def) {...});の構築が必要になることはめったにありませんが、状況によっては非常に役立つと思います。

まず、:

_return $.Deferred(function(def) { def.resolve(); }).promise();
_

に簡素化されます:

_return $.Deferred().resolve().promise();
_

次に、DataService.makeRequest()では、次のように.then()を利用することで、明示的な_$.Deferred_の必要性を回避できます。

_function DataService() {
    var self = this;
    self.makeRequest = function (functionName, data) {
        return $.ajax({
            type: "POST",
            url: "WebService.asmx/" + functionName,
            data: JSON.stringify(data),
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        }).then(null, function (xhr, status, error) {
            var ex;
            try {
                ex = eval("(" + xhr.responseText + ")");
                ex.message = ex.Message;
                ex.Message = undefined;
            } catch (ex2) {
                ex = {
                    message: "Invalid Response From Server:\r\n" + xhr.responseText
                };
            }
            if (ex.message == "LoginRequired") {
                app.viewModels.main.loginRequired(true);
            } else {
                app.showError(ex.message);
            }

            return ex.message;
        });
    };
}
_

主な側面は次のとおりです。

  • $.ajax()は、Promise互換のjqXHRオブジェクトを返します。これは(成功またはエラーで).then()によって即座に処理され、変更されます。
  • .then(null, ...)-新しいプロミスが渡され、$.ajax()によって返される元のプロミスと同じ値で解決されます。したがって、「完了」(つまり、ajaxの成功)状態では、.then()は完全に透過的です。
  • _return ex.message;_-新しいプロミスが渡され、_ex.message_で拒否されます。

ただし、nett効果は元のコードと同じである必要があります。IMHOの.then()のチェーンは、$.Deferred()コールバック内ですべてを設定するよりもかなりクリーンです。

ところで、 '。Message'がstatus引数(または_xhr.status_ ??)として直接表示されるように適切なHTTPヘッダーをサーバー側に設定することで、eval("(" + xhr.responseText + ")")などの必要性を回避できる場合があります)失敗コールバックの。たとえば、PHPでは次のようになります。

_$message = "my custom message";
header("HTTP/1.1 421 $message");
exit(0);
_

ASPは同じ機能を提供します。

IIRC、4xxシリーズのステータスコードはすべてその役割を果たします。 421はたまたま特定の意味のないものです。

49

単にreturn $.when();を使用して既に解決された約束を返します。

引数をまったく渡さない場合、jQuery.when()は解決されたプロミスを返します。

リファレンス:https://api.jquery.com/jquery.when/


注:

  • これはreturn $.when(undefined);と同じです。これは、配列とapplyの使用を回避する次のかなりクールなトリックにつながります。

可変数のプロミスが完了するのを待ちたい場合in parallelループでこのパターンを使用できます:

var promise;   // Undefined is treated as an initial resolved promise by $.when
while (insomeloop){
    promise = $.when(promise, newpromise);
}

次に、完了時に最終呼び出しを行います。

promise.then(function(){
    // All done!
});

例えば.

var promise;$.when
var $elements = $('.some-elements');
$elements.each(function(){
     var $element = $(this).fadeout();
     promise = $.when(promise, $element.promise());
});
promise.then(function(){
    // All promises completed!
});

欠点は軽微です:

  • whenを呼び出すたびに、以前のプロミスが新しいプロミスにラップされます。わずかなオーバーヘッドで、一連のプロミスを維持および評価する必要がなくなりました。
  • 最終関数に直接値を渡すことはできません。通常はparallel promisesからの戻り値が必要ないため、これはマイナーです。
  • 単一のプロミスが失敗すると、最終待機が早期に中止されるため、すべてのプロミスが確実に満たされるようにする必要があります。
26
Gone Coding