为什么回调比承诺更"tightly coupled"?



你能解释一下下面的短语吗(取自Stack Overflow问题的答案Javascript中的Deferred、Promise和Future之间有什么区别?)?

使用jQuery promise与使用以前的jQuery回调相比有什么好处?

而不是直接将回调传递给函数可以导致紧密耦合的接口,使用promise可以分别关注同步或异步代码。

promise是一个表示异步操作结果的对象,因此您可以传递它,这给了您更多的灵活性。

如果使用回调,那么在调用异步操作时,必须指定如何处理它,从而指定耦合。通过promise,您可以指定以后将如何处理它。

这里有一个例子,假设您想要通过ajax加载一些数据,并且在执行此操作时,您想要显示一个加载页面。

使用回调:

void loadData = function(){
showLoadingScreen();
$.ajax("http://someurl.com", {
complete: function(data){
hideLoadingScreen();
//do something with the data
}
});
};

处理返回数据的回调必须调用hideLoadingScreen。

有了promise,您可以重写上面的代码片段,使其变得更可读,并且不必将hideLoadingScreen放在完整的回调中。

承诺

var getData = function(){
showLoadingScreen();
return $.ajax("http://someurl.com").promise().always(hideLoadingScreen);
};
var loadData = function(){
var gettingData = getData();
gettingData.done(doSomethingWithTheData);
}
var doSomethingWithTheData = function(data){
//do something with data
};

更新:我写了一篇博客文章,提供了额外的例子,并清楚地描述了什么是承诺,以及如何将其使用与使用回调进行比较。

promise的耦合更松散,因为操作不必"知道"如何继续,只需要知道何时准备好。

当您使用回调时,异步操作实际上有一个对其延续的引用,这不是它的业务。

使用promise,您可以在决定如何解决异步操作之前,轻松地在异步操作上创建一个表达式。

因此,承诺有助于将链接事件与实际工作的关注点区分开来。

我不认为promise与回调或多或少是耦合的,只是大致相同。

然而,承诺还有其他好处:

  • 如果您公开了一个回调,您必须记录它是被调用一次(如在jQuery.ajax中)还是被调用多次(如在Array.map中)。Promises总是调用一次。

  • 没有办法调用回调抛出和异常,所以必须为错误情况提供另一个回调。

  • 只可以注册一个回调,多个用于promise,您可以在事件发生后注册它们,无论如何都会被调用。

  • 在类型化声明(Typescript)中,Promise使签名更容易阅读。

  • 将来,您可以利用异步/屈服语法。

  • 因为它们是标准的,你可以制作这样一个可重复使用的组件:

    disableScreen<T>(promiseGenerator: () => Promise<T>) : Promise<T>
    {
    //create transparent div
    return promiseGenerator.then(val=>
    {
    //remove transparent div
    return val;
    }, error=>{
    //remove transparent div
    throw error;
    });
    }
    disableScreen(()=>$.ajax(....));
    

更多信息:http://www.html5rocks.com/en/tutorials/es6/promises/

编辑:

  • 另一个好处是编写一系列N个异步调用,而不需要N级缩进

此外,虽然我仍然不认为这是重点,但现在我认为它们的耦合有点松散,原因如下:

  • 它们是标准的(或者至少是尝试过的):C#或Java中使用字符串的代码比C++中的类似代码耦合得更糟糕,因为字符串的不同实现使其更易于重用。有了一个标准的promise,调用者和实现之间的耦合就更少了,因为它们不必在一对具有自定义参数、顺序、名称等的自定义回调上达成一致。promise有很多不同的风格,这一事实无助于思考。

  • 它们促进了更基于表达式的编程,更易于编写、缓存等。:

    var cache: { [key: string] : Promise<any> };
    function getData(key: string): Promise<any> {
    return cache[key] || (cache[key] = getFromServer(key)); 
    }
    

您可以争辩说,基于表达式的编程比基于命令式/回调的编程更松散地耦合,或者至少它们追求相同的目标:可组合性。

承诺具体化了延迟响应的概念。它们使异步计算成为一流的公民,因为您可以四处传递它。如果你想要一元结构,它们允许你定义结构,在这个结构上你可以构建更高阶的组合子,极大地简化代码。

例如,您可以有一个函数,它接受一个promise数组并返回一个数组的promise(通常称为sequence)。这是很难做到的,甚至不可能与回调。这样的组合子不仅使代码更容易编写,而且使代码更易于阅读。

现在换一种方式来回答你的问题。回调是一种特殊的解决方案,承诺允许更清晰的结构和可重用性。

它们不是,这只是一种合理化,那些完全没有达到承诺的人用它来证明写的代码比用回调写的代码多得多。考虑到这样做显然没有任何好处,您至少可以始终告诉自己代码的耦合性较低或其他什么。

看看什么是承诺,我为什么要把它们用于实际的具体利益。

最新更新