promise の例を純粋な 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);
});
Norbert Hartl が指摘したように、JSON.parse() は大きな文字列に対してブラウザーをハングアップさせます。そのため、setTimeout() を使用して実行を遅らせました (10 ミリ秒の一時停止の後)。これは、Kris Kowal のソリューションの一例です。これにより、コールバックが実行される前に、現在の Javascript スレッドが完了し、ブラウザーが解放されて DOM の変更を表示し、ユーザーのためにページをスクロールできるようになります。
commonjs promise フレームワークも setTimeout のようなものを使用することを願っています。そうしないと、記事の例の後半の promise が実際に同期的に実行されてしまいます。
上記の私の代替案はかなり見苦しく、後のプロセスではさらにインデントが必要です。プロセス チェーンをすべて 1 つのレベルで提供できるように、コードを再構築しました。
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 での従来のコールバックのフォワード パッシングがプロミスとほとんど同じであることを実証したいと考えていました。しかし、2 回の試行の後、元の例のコードの簡潔さを参照して、promise がはるかに洗練された解決策であることを示したようです!