Подтвердить что ты не робот

Возвращение уже разрешенных обещаний

У меня есть существующий проект с множеством асинхронных функций, возвращающих promises. Я добавляю некоторое кэширование, чтобы в некоторых случаях асинхронные функции выполнялись синхронно и хотели бы сделать этот код короче/лучше, если это возможно:

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

Например, у меня есть класс Data Service, который обрабатывает большинство запросов AJAX, которые выглядят следующим образом:

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, а вместо этого просто выполняет некоторую синхронную работу и затем возвращает. Он должен вернуть обещание, хотя, поскольку все, что его назвало, будет ожидать этого, но оно должно быть "уже завершенным/разрешенным обещанием". В настоящее время я использую первый набор кода выше, чтобы сделать это. Похоже, что должен быть лучший способ.

4b9b3361

Ответ 1

@Eselk,

По моему опыту, конструкция $.Deferred(function(def) {...}); редко нужна, хотя я думаю, что она может быть весьма полезна в некоторых случаях.

Во-первых,

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

упростит:

return $.Deferred().resolve().promise();

Во-вторых, в DataService.makeRequest() вы можете избежать необходимости явного $.Deferred, используя .then(), как показано ниже:

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() возвращает совместимый с обещанием объект jqXHR, который (при успехе/ошибке) немедленно действует и изменяется с помощью .then().
  • .then(null, ...) - инициирует новое обещание, разрешенное с теми же значениями, что и исходное обещание, возвращаемое $.ajax(). Таким образом, в соответствии с условием "done" (т.е. ajax success), .then() является полностью прозрачным.
  • return ex.message; - заставляет новое обещание быть передано, отклонено с помощью ex.message.

Эффект nett должен быть идентичен вашему исходному коду, хотя IMHO, цепочка .then() значительно чище, чем настройка всего внутри обратного вызова $.Deferred().

Кстати, вы можете избежать необходимости в eval("(" + xhr.responseText + ")") и т.д., установив соответствующий сервер HTTP-заголовка таким образом, чтобы ваш ".Message" отображался непосредственно как аргумент status (или xhr.status? ) обратного вызова отказа. Например, в PHP вы бы сделали что-то вроде:

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

Я уверен, что ASP предлагает ту же возможность.

IIRC, любой код состояния серии 4xx выполнит эту работу. 421 просто оказывается без особого смысла.

Ответ 2

Просто используйте return $.when();, чтобы вернуть уже разрешенное обещание.

Если вы не передадите ему никаких аргументов, jQuery.when() вернет разрешенное обещание.

Ссылка: https://api.jquery.com/jquery.when/


Примечания:

  • Это то же самое, что и return $.when(undefined);, что приводит к следующему довольно крутому трюку, который позволяет избежать использования массивов и apply.

Если вы хотите, чтобы переменное число promises выполнялось параллельно, вы можете использовать этот шаблон в цикле:

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 завершает предыдущее обещание в новом обещании. Небольшие накладные расходы, и вам больше не нужно поддерживать и оценивать массив promises.
  • никакие значения не могут быть напрямую переданы конечной функции. Поскольку вы обычно не хотите возвращать значения из параллельного promises, это незначительно.
  • отказ от единого обещания будет прервать окончательное ожидание раньше, поэтому вам необходимо обеспечить выполнение всех promises.