【问题标题】:What is the benefit of a 'promise' abstraction in CommonJS?CommonJS 中的“承诺”抽象有什么好处?
【发布时间】:2011-01-10 17:35:18
【问题描述】:

我正在阅读this article,关于承诺抽象的部分对我来说似乎有点过于复杂。下面举例说明:

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);
    });

【问题讨论】:

  • 你是对的,使用 Promise 进行同步操作是没有意义的。所以结果应该是相等的。但这是一个示例,并说明了 Promise 的用法。对于在您的示例之后运行的代码,确实存在差异。如果您需要在示例之后运行某些东西,那么您可以在不知道示例代码在做什么的情况下(通过使用 promise 方法)执行它

标签: javascript abstraction commonjs promise


【解决方案1】:

虽然两者最终都会完成相同的事情,但不同之处在于您的第二个示例不是异步的。例如,考虑如果JSON.parse(...) 被证明是一个极其昂贵的操作会发生什么;你必须挂起,直到一切都完成,这可能并不总是你想要的。

这就是承诺为您带来的:将正确答案的计算推迟到更方便的时间的强大能力。顾名思义,该构造“承诺”在某个时候给你结果,只是现在不一定。您可以阅读更多关于更大规模的期货和承诺工作here

【讨论】:

  • 这里什么时候方便?如果操作非常昂贵并且 JSON.parse 是一段 javascript 代码,它无论如何都会挂起。不同的是,有了promise你就可以完成实际运行的功能。
  • 确实,无论是同步计算还是异步计算,解析都将花费相同的时间。但是,如果您花时间以这样一种方式实现解析器,即它在完成小部分操作后可预测地屈服于事件循环,则其他异步代码可以在每个块之间异步运行。这使应用程序的响应速度更快,而不是更快。
  • 或者 JSON.parse 可以是本地方法并在另一个线程上执行
  • @Jan 这没有帮助。 JSON.parse 已经是原生方法了。
  • 这更适合未来阅读您评论的人。您可以创建一个调用本机 JSON.parse 的 Web Worker,以防止 UI 线程挂起。这将涉及加载新的工作程序,附加新消息的侦听器,在工作程序中调用 JSON.parse,从工作程序发布结果,然后将结果传递给需要它的原始代码。将这段代码包装在一个 Promise 中可以抽象出这个过程,并且可以更容易地编写/重构/读取代码所做的事情,而无需调试混乱。
【解决方案2】:

让我们将 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确实会像担心的那样同步运行。

我上面的替代方案看起来很丑,后面的过程需要进一步缩进。我重组了代码,这样我们就可以在一个层次上提供我们的流程链:

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 中传统的前向回调与 Promise 相当。然而,经过两次尝试,我似乎已经证明,参考原始示例中代码的简洁性,promise 是一个更优雅的解决方案!

【讨论】:

    【解决方案3】:

    第二个 sn-p 容易受到拒绝服务攻击,因为 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);
        });
    

    【讨论】:

      【解决方案4】:

      可能还需要补充一点,第一个版本相对于第二个版本的优势在于它分离了细化链中的不同操作(函数也不必就地编写)。第二个版本将低级解析与应用程序逻辑混合在一起。具体来说,使用 SOLID 原则作为指导,第二个版本违反了OCPSRP

      【讨论】:

        猜你喜欢
        • 2012-11-22
        • 1970-01-01
        • 2011-11-14
        • 1970-01-01
        • 2010-11-16
        • 1970-01-01
        • 2010-09-20
        • 2010-12-31
        • 1970-01-01
        相关资源
        最近更新 更多