为什么回调比承诺更"紧密耦合"?

Rev*_*ous 35 javascript jquery callback promise

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

使用jQuery promises反对使用以前的jQuery回调有什么优点?

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

Rui*_*Rui 49

promise是一个表示异步操作结果的对象,因此可以传递它,这样可以提供更大的灵活性.

如果使用回调,则在调用异步操作时必须指定如何处理它,从而指定耦合.有了承诺,您可以指定以后如何处理.

这是一个例子,想象你想通过ajax加载一些数据,同时你想要显示一个加载页面.

回调:

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

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

使用promises,您可以重写上面的代码段,使其更具可读性,并且您不必将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
};
Run Code Online (Sandbox Code Playgroud)

更新:我写了一篇博文,提供了额外的例子,并提供了一个明确的描述,即什么是一个承诺,以及如何将其使用与使用回调进行比较.

  • 这些示例并不是真正等效的,通过为回调版本提取getData,您可以获得相同的好处,https://gist.github.com/opsb/9413093 (17认同)

har*_*rpo 27

耦合对承诺更宽松,因为操作不必"知道"它是如何继续的,它只需知道它何时准备就绪.

当您使用回调时,异步操作实际上引用了它的继续,这不是它的业务.

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

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


Olm*_*lmo 12

我不认为承诺比回调更多或更少耦合,几乎相同.

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

  • 如果你公开一个回调,你必须记录它是否会被调用一次(比如在jQuery.ajax中)或者不止一次(比如在Array.map中).承诺总是被召唤一次.

  • 没有办法在其上调用回调抛出和异常,因此您必须为错误情况提供另一个回调.

  • 只需要注册一个回调,不止一个回复,你可以在事件发生后注册它们,无论如何你都会被调用.

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

  • 将来,您可以利用async/yield语法.

  • 因为它们是标准的,所以你可以制作像这样的可重用组件:

     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(....));
    
    Run Code Online (Sandbox Code Playgroud)

更多内容:http://www.html5rocks.com/en/tutorials/es6/promises/

编辑:

  • 另一个好处是编写一系列N个异步调用而没有N级缩进.

此外,虽然我仍然认为这不是主要观点,但现在我认为它们因为这个原因而松散耦合:

  • 它们是标准的(或者至少是尝试):使用字符串的C#或Java中的代码比C++中的类似代码更糟糕,因为字符串的不同实现使其更具可重用性.有一个标准的承诺,调用者和实现彼此之间的耦合较少,因为他们不必同意自定义参数命令,名称等的(一对)自定义回调...事实上有很多不同承诺的味道无济于事.

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

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

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

  • 不是真的,使用`yield`你会遇到语法try-catch而使用箭头函数时,代码行和详细程度保持不变. (2认同)

edo*_*fic 5

Promise将延迟响应的概念归结为某种东西.他们使异步计算成为一流的公民,因为你可以传递它.它们允许您根据需要定义结构 - 一元结构 - 您可以在其上构建更高阶的组合器,从而大大简化代码.

例如,你可以有一个函数,它接受一个promises数组并返回一个数组的promise(通常叫做sequence).回调很难做甚至不可能.而这样的组合器不仅使代码更容易编写,而且使它更易于阅读.

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