Ответ 1
Обновление: jQuery 3.0 исправил проблемы, описанные ниже. Это действительно соответствует Promises/A +.
Да, jQuery promises имеют серьезные и неотъемлемые проблемы.
Тем не менее, поскольку статья была написана, jQuery приложил значительные усилия, чтобы быть более жалобными Promises/Aplus, и теперь у них есть метод .then, который цепочки.
Таким образом, даже в jQuery returnsPromise().then(a).then(b)
для функций возврата обещание a
и b
будет работать, как ожидалось, разворачивая возвращаемое значение перед продолжением вперед. Как показано на рисунке fiddle:
function timeout(){
var d = $.Deferred();
setTimeout(function(){ d.resolve(); },1000);
return d.promise();
}
timeout().then(function(){
document.body.innerHTML = "First";
return timeout();
}).then(function(){
document.body.innerHTML += "<br />Second";
return timeout();
}).then(function(){
document.body.innerHTML += "<br />Third";
return timeout();
});
Однако две огромные проблемы с jQuery - это обработка ошибок и неожиданный порядок выполнения.
Обработка ошибок
Невозможно отметить обещание jQuery, которое отклонено как "обработанное", даже если вы разрешите его, в отличие от catch. Это делает отклонения в jQuery по своей сути сломанными и очень сложными в использовании, не что иное, как синхронный try/catch
.
Можете ли вы догадаться, какие журналы здесь? (fiddle)
timeout().then(function(){
throw new Error("Boo");
}).then(function(){
console.log("Hello World");
},function(){
console.log("In Error Handler");
}).then(function(){
console.log("This should have run");
}).fail(function(){
console.log("But this does instead");
});
Если вы догадались "uncaught Error: boo"
, вы были правы. jQuery promises не выбрасывать безопасно. Они не позволят вам обрабатывать любые брошенные ошибки, в отличие от Promises/Aplus promises. Как насчет отказа от безопасности? (fiddle)
timeout().then(function(){
var d = $.Deferred(); d.reject();
return d;
}).then(function(){
console.log("Hello World");
},function(){
console.log("In Error Handler");
}).then(function(){
console.log("This should have run");
}).fail(function(){
console.log("But this does instead");
});
Следующие журналы "In Error Handler" "But this does instead"
- вообще не существует способа обращения с отказом обещания jQuery. Это не похоже на поток, который вы ожидаете:
try{
throw new Error("Hello World");
} catch(e){
console.log("In Error handler");
}
console.log("This should have run");
Каков поток, который вы получаете с помощью библиотек Promises/A +, таких как Bluebird и Q, и того, что вы ожидаете от полезности. Это огромно, и бросить безопасность - это большой пункт продажи для promises. Вот Bluebird, действующий правильно в этом случае.
Порядок выполнения
jQuery будет выполнять переданную функцию немедленно, а не откладывать ее, если базовое обещание уже разрешено, поэтому код будет вести себя по-разному в зависимости от того, будет ли обещание, которое мы прикрепляем обработчика к отклоненному, уже разрешено. Это эффективно освобождение Zalgo и может вызвать некоторые из самых болезненных ошибок. Это создает некоторые из наиболее сложных для отладки ошибок.
Если мы посмотрим на следующий код: (fiddle)
function timeout(){
var d = $.Deferred();
setTimeout(function(){ d.resolve(); },1000);
return d.promise();
}
console.log("This");
var p = timeout();
p.then(function(){
console.log("expected from an async api.");
});
console.log("is");
setTimeout(function(){
console.log("He");
p.then(function(){
console.log("̟̺̜̙͉Z̤̲̙̙͎̥̝A͎̣͔̙͘L̥̻̗̳̻̳̳͢G͉̖̯͓̞̩̦O̹̹̺!̙͈͎̞̬ *");
});
console.log("Comes");
},2000);
Мы можем наблюдать, что oh настолько опасное поведение, setTimeout
ждет завершения первоначального таймаута, поэтому jQuery переключает свой порядок выполнения, потому что... кому нравятся детерминированные API, которые не вызывают переполнение стека? Вот почему спецификация Promises/A + требует, чтобы promises всегда откладывались до следующего выполнения цикла событий.
Боковое примечание
Стоит упомянуть, что новые и более сильные библиотеки обещаний, такие как Bluebird (и экспериментально When), не требуют .done
в конце цепочки, как Q, поскольку они сами определяют необработанные отказы, они также намного быстрее, чем jQuery promises или Q promises.