Вернуть уже решенное обещание
у меня есть существующий проект, который имеет много асинхронных функций, которые возвращают обещания. Я добавляю некоторое кэширование, чтобы в некоторых случаях асинхронные функции выполнялись синхронно и хотели бы сделать этот код короче / лучше, если это возможно:
return $.Deferred(function(def) { def.resolve(); }).promise();
например, у меня есть класс службы данных, который обрабатывает большинство запросов 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:rn" + 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, а вместо этого просто выполняет некоторую синхронную работу, а затем возвращается. Однако он должен вернуть обещание, потому что то, что он назвал, будет ожидать этого, но это должно быть "уже завершенное/разрешенное обещание". В настоящее время я использую первый набор кода выше, чтобы сделать это. Кажется, должен быть лучший способ.
2 ответов
@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()
. Таким образом, при условии "сделано" (т. е. успех ajax),.then()
полностью прозрачна. -
return ex.message;
- вызывает новое обещание, которое будет передано, отклонено сex.message
.
влияние Нетт должно быть идентично к вашему оригиналу код, однако, ИМХО, цепочка .then()
значительно чище, чем все, что внутри $.Deferred()
обратный.
кстати, вы можете избежать необходимости eval("(" + xhr.responseText + ")")
etc, установив соответствующий HTTP-заголовок на стороне сервера, такой, что ваш '.Сообщение " отображается непосредственно как
просто использовать return $.when();
вернуться уже решенное обещание.
если вы не передадите ему никаких аргументов вообще, jQuery.когда() будет возвращать решительное обещание.
ссылки: https://api.jquery.com/jquery.when/
Примечания:
- это то же самое, что
return $.when(undefined);
что приводит к следующим довольно крутой трюк, который позволяет избежать любого применения массивов иapply
.
если вы хотите дождаться переменного числа обещаний для завершения параллельно затем вы можете использовать этот шаблон в цикле:
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
обертывает предыдущее обещание в новое обещание. Незначительные накладные расходы, и вам больше не нужно поддерживать и оценивать множество обещаний. - никакие значения не могут быть непосредственно переданы в конечную функцию. Как правило, возвращаемые значения из параллельные обещания, это мелочи.
- невыполнение одного обещания прервет окончательное ожидание рано, поэтому вам нужно убедиться, что все обещания будут выполнены.