Давайте сравним пример обещания с чистым примером Javascript:
// First we need a convenience function for W3C's fiddly XMLHttpRequest.
// It works a little differently from the promise framework. Instead of
// returning a promise to which we can attach a handler later with .then(),
// the function accepts the handler function as an argument named 'callback'.
function requestSomeDataAndCall(url, callback) {
var req = new XMLHttpRequest();
req.onreadystatechange = resHandler;
req.open("GET", url, false);
req.send();
function resHandler() {
if (this.readyState==4 && this.status==200) {
callback(this);
} else {
// todo: Handle error.
}
}
}
requestSomeDataAndCall("http://example.com/foo", function(res){
setTimeout(function(){
var data = JSON.parse(res.responseText);
setTimeout(function(){
var price = data.price;
setTimeout(function(){
print("The price is "+price);
},10);
},10);
},10);
});
Как отметил Норберт Хартл, JSON.parse () повесит браузер для больших строк. Поэтому я использовал setTimeout (), чтобы задержать его выполнение (после паузы в 10 миллисекунд). Это один из примеров решения Криса Ковала. Он позволяет завершить текущий поток Javascript, освобождая браузер для представления изменений DOM и прокрутки страницы для пользователя до запуска обратного вызова.
Я надеюсь, что в структуре обещаний commonjs также используется что-то вроде setTimeout, в противном случае последующие обещания в примере статьи будут действительно работать синхронно, как и опасалось.
Моя альтернатива, представленная выше, выглядит довольно уродливо, поскольку последующие процессы требуют дополнительного отступа. Я реструктурировал код, чтобы мы могли предоставить нашу цепочку процессов на одном уровне:
function makeResolver(chain) {
function climbChain(input) {
var fn = chain.shift(); // This particular implementation
setTimeout(function(){ // alters the chain array.
var output = fn(input);
if (chain.length>0) {
climbChain(output);
}
},10);
}
return climbChain;
}
var processChain = [
function(response){
return JSON.parse(response.body);
},
function(data){
return data.price; // get the price
},
function(price){
print("The price is " + price);
}
];
var climber = makeResolver(promiseChain);
requestSomeDataAndCall("http://example.com/foo", climber);
Я надеялся продемонстрировать, что традиционная пересылка обратных вызовов в Javascript в значительной степени эквивалентна обещаниям. Однако после двух попыток, как мне показалось, со ссылкой на аккуратность кода в исходном примере, эти обещания являются гораздо более элегантным решением!