13

私はこの記事を読んでいますが、promise の抽象化に関するセクションは少し複雑すぎるように思えます。以下に例を示します。

requestSomeData("http://example.com/foo") // returns a promise for the response
    .then(function(response){ // ‘then’ is used to provide a promise handler
        return JSON.parse(response.body); // parse the body
    }) // returns a promise for the parsed body
    .then(function(data){
        return data.price; // get the price
    }) // returns a promise for the price
    .then(function(price){ // print out the price when it is fulfilled
        print("The price is " + price);
    });

以下は、より少ないコード行で同じ結果を提供できるように思えます。

requestSomeData("http://example.com/foo")
    .requestHandler(function(response){
        // parse the body
        var data  = JSON.parse(response.body);

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });
4

4 に答える 4

17

どちらも最終的に同じことを達成することは事実ですが、2 番目の例は非同期ではないという違いがあります。たとえばJSON.parse(...)、非常にコストのかかる操作であることが判明した場合にどうなるかを考えてみましょう。すべてが完了するまでハングアップする必要がありますが、これは常に希望どおりになるとは限りません。

それが、約束があなたにもたらすものです: より都合の良い時まで正しい答えの計算を延期する強力な能力です. 名前が示すように、コンストラクトはある時点で結果を提供することを「約束」しますが、必ずしも今すぐではありません。Futures と Promise のより大きなスケールでの作業については、こちらで詳しく読むことができます。

于 2010-01-29T05:41:00.273 に答える
3

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 がはるかに洗練された解決策であることを示したようです!

于 2011-11-19T16:03:03.143 に答える
1

2 番目のスニペットは、サービス拒否攻撃に対して脆弱です。これは、example.com/foo が無効な json を返すだけでサーバーがクラッシュする可能性があるためです。空の応答でさえ無効な JSON です (ただし、有効な JS)。mysql_*SQL インジェクション ホールが目立つ例のようなものです。

また、promise コードも大幅に改善できます。これらは等しい:

requestSomeData("http://example.com/foo") // returns a promise for the response
    .then(function(response){ // ‘then’ is used to provide a promise handler
        // parse the body
        var data  = JSON.parse(response.body);

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });

と:

requestSomeData("http://example.com/foo")
    .requestHandler(function(response){
        try {
            var data = JSON.parse(response.body);
        }
        catch(e) {
            return;
        }

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });

エラーを処理したい場合、これらは等しくなります。

requestSomeData("http://example.com/foo") // returns a promise for the response
    .then(function(response){ // ‘then’ is used to provide a promise handler
        // parse the body
        var data  = JSON.parse(response.body);

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    }).catch(SyntaxError, function(e) {
        console.error(e);
    });

と:

requestSomeData("http://example.com/foo")
    .requestHandler(function(response){
        try {
            var data = JSON.parse(response.body);
        }
        catch(e) {
            //If the above had a typo like `respons.body`
            //then without this check the ReferenceError would be swallowed
            //so this check is kept to have as close equality as possible with
            //the promise code
            if(e instanceof SyntaxError) {
                console.error(e);
                return;
            }
            else {
                throw e;
            }
        }

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });
于 2013-11-09T12:57:57.700 に答える
0

また、最初のバージョンが2番目のバージョンよりも優れている点は、リファインメントチェーン内のさまざまな操作を分離できることです(関数をインプレースで記述する必要もありません)。2番目のバージョンは、低レベルの解析とアプリケーションロジックの両方を組み合わせたものです。具体的には、SOLIDの原則をガイドラインとして使用すると、2番目のバージョンはOCPSRPの両方に違反します。

于 2013-01-23T10:17:52.233 に答える