回调或承诺?

时间:2014-06-18 00:59:21

标签: javascript callback promise

在使用javascript异步错误捕获机制时,我最终问自己使用promise而不是回调之间的区别,除了事实承诺可能更多的糖语法。

例如,让我们考虑一下

        function setPromise() {
            var message = "awesome";
            var deferred = new $.Deferred();
            setTimeout(function(){deferred.resolve(message)},3000);
            return deferred.promise();
        }

        var promise = setPromise();

        promise.done(function (message) {
            console.log("promise done with message : " + message);
        });

        function setCallback(doneCallback) {
            var message = "awesome";
            setTimeout(function(){doneCallback(message)},3000);
        }

        setCallback(callback)

        function callback(message) {
            console.log("callback done with message : " + message);
        }

两者都充当闭包,都允许发回参数等等。

那有什么区别?

2 个答案:

答案 0 :(得分:5)

Promise建立在回调之上。后者更原始,更通用,当你需要做一些复杂的事情时需要做更多的工作。

对于你的例子,他们做了几乎相同的事情。但是,假设您希望同时解决三个问题(想象同时通过AJAX请求三个资源),并在完成所有这三个操作时继续。承诺是微不足道的,因为基本上没有任何改变;但是使用回调,你需要设置一些标志/计数器,并自己识别成功和失败状态 - 更多的工作。

答案 1 :(得分:3)

从语义上讲,这两段代码之间并没有真正的区别。在调用初始函数后的某个时间,消息将提供给回调。

从设计的角度来看,人们倾向于支持承诺,因为它们通常会更容易遵循代码。在回调处理某些长时间运行的函数的结果时尤其如此。考虑以下两个慢速运行的函数:

var slowlyReturn1 = function (callback) {
  window.setTimeout(callback.call(1), 1000);
}

var slowlyReturn2 = function (callback) {
  window.setTimeout(callback.call(2), 1000);
}

使用这两个长时间运行的函数的结果编写代码非常多毛:

slowlyReturn1(function(resultOf1) {
  slowlyReturn2(function(resultOf2) {
    console.log("results were: " + resultOf1 + " and " + resultOf2);
  })
});

注意长期运行的函数链中的每个链接如何导致另一个嵌套级别。使用承诺代码,您往往不会遇到此问题:

var slowlyReturn1 = function () {
  var d = $.Deferred();
  window.setTimeout(function () { d.resolve(1) }, 1000);
  return d.promise();
}

var slowlyReturn2 = function () {
  var d = $.Deferred();
  window.setTimeout(function () { d.resolve(2) }, 1000);
  return d.promise();
}

var resultOf1;

slowlyReturn1().then(function(r) {
  resultOf1 = resultOf1;
  return slowlyReturn2();
}).then(function(resultOf2) {
  console.log("results were: " + resultOf1 + " and " + r);
});

此外,使用承诺代码,往往会更清晰地分离关注点。执行慢速运行的代码并不知道如何使用结果:它只返回表示延迟结果的内容并让调用者处理它。

这个你处理它设计的一个很好的应用程序围绕异常处理。缓慢运行的操作可以.resolve()承诺,但他们也可能.reject()出现问题。可以使用.fail()处理此拒绝,如下所示:

slowRunningOperations().then(function () {
  ...
  ...
  ... handle success
  ...
  ...
}).fail(function() {
  ...
  ... handle failure
  ...
  ...
})

在这里,慢速运行的调用者并不关心错误,它可以简单地忽略它们。

承诺编程还有其他一些好处:

  • 大多数支持promises的库提供了一种方法来处理以相同方式返回promise的常规函数​​和函数。他们通常通过提供名为when()的函数来实现此目的。这提供了一种非常好的方法来测试promise代码,或者允许将一个缓慢的函数更改为promise-returns-one,而不会影响调用者。

  • 大多数支持promises的库还提供了使用promises来模拟更传统控制流的功能。例如,Q库提供allSettled(list),它接受​​一个承诺列表并返回一个承诺,该承诺在列表中的所有承诺完成后解析。

那就是说,正如另一个答案所说,承诺会带来一些开销。如果您没有进行强烈的链接或错误处理,或者您严格使用回调控制流,那么您最好只传递函数。