异步JavaScript - 回调函数 vs 延迟/承诺

51

可能是重复问题:
JavaScript中Deferred、Promise和Future的区别是什么?

最近我在努力提高我的JavaScript应用程序的质量。

我采用的一个模式是使用单独的“数据上下文”对象来为我的应用程序加载数据(之前我是直接在我的视图模型中进行操作)。

以下示例返回在客户端上初始化的数据:

var mockData = (function($, undefined) {

    var fruit = [
        "apple",
        "orange",
        "banana",
        "pear"
        ];

    var getFruit = function() {
        return fruit;
    };

    return {
        getFruit: getFruit
    }
})(jQuery);

在大多数情况下,我们将从服务器加载数据,因此无法立即返回响应。在我们的API中处理这个问题似乎有两个选项:

  1. 使用回调函数
  2. 返回一个promise

以前我总是使用回调函数的方法:

var getFruit = function(onFruitReady) {
    onFruitReady(fruit);
};

// ...

var FruitModel = function(dataContext, $) {
    return {
        render: function() {
            dataContext.getFruit(function(fruit) {
                // do something with fruit
            });
        }
    };
};

然而,我能理解在构建复杂的 JavaScript 应用程序时如何陷入回调地狱的可能性。

后来,我了解了 Promises 设计模式。不再要求调用者提供回调函数,而是返回一个可观察的“promise”:

var getFruit = function() {
    return $.Deferred().resolve(fruit).promise();
};

// ...
dataContext.getFruit().then(function(fruit) {
    // do something with fruit
});

我可以看出使用这种模式的明显好处,特别是因为我可以在多个延迟对象上等待wait,这在加载单页应用程序的初始化数据时非常有用。

然而,在我开始愤怒地使用任何一种模式之前,我很想了解每种模式的优缺点。我也对其他库是否朝着这个方向发展感兴趣。jQuery似乎是这样的情况。

这是一个链接,指向我用于测试的Fiddle。


4
好消息:jQuery的ajax API已经自带Promises功能! - Pointy
在这里阅读有关该机制的内容:http://api.jquery.com/jQuery.ajax/#jqXHR - Pointy
啊,我的意思是你不需要添加太多内容;这些调用的返回值已经可以直接使用了,所以只需要养成不再传递“成功”回调函数的习惯,而是开始调用.done()等方法即可。 - Pointy
@jbabey 不是重复问题:deferred/promise/future 属于 promises 设计模式,OP 正在询问与直接回调的比较。 - Christophe
参考:http://softwareengineering.stackexchange.com/questions/302455/is-there-really-a-fundamental-difference-between-callbacks-and-promises回调和承诺之间真的有根本区别吗? - Mahesh K
显示剩余6条评论
2个回答

18

承诺(Promise)在幕后也依赖于回调函数,因此它们并不是互相排斥的。

回调函数的好处在于使用纯JavaScript很容易实现(例如在ajax调用中)。

承诺需要额外的抽象层,通常意味着你将依赖一个库(在你的情况下,你已经使用jQuery,所以这不是问题)。它们非常适合处理多个异步调用并行执行的情况。


9
请注意,截至2014年3月,浏览器已开始实现本地Promise,因此我的陈述仅适用于polyfill。 - Christophe

3

经过阅读@Pointy提供的jQuery文档,听起来Deferred API的区别在于它允许您指定多个函数在请求完成时被调用:

自jQuery 1.5起,错误(fail)、成功(done)和完成(always,自jQuery 1.6起)回调钩子是先进先出的管理队列。这意味着您可以为每个钩子分配多个回调。请参见Deferred对象方法,它们是为这些$.ajax()回调钩子内部实现的。

另请参见:deferred.then()


1
Deferreds / promises 不仅允许您指定要调用的多个函数,而且使这样做更加容易。特别的功能是(a)可以在代码中的任何位置添加函数(例如.done().fail()),只要 Deferred / promise 在范围内即可,(b)在解决/拒绝延迟后添加的函数将立即触发。通过公开其内部的.Callbacks()实用程序,jQuery使得不使用Deferreds / promises也可以实现此类功能,但它们使编码/调试变得轻松愉快。 - Beetroot-Beetroot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接