JavaScript 中的 Deferred、Promise 和 Future 之间有什么区别?

IT技术 javascript promise future deferred
2021-01-29 23:14:36

Deferreds、Promises 和 Futures 之间有什么区别?
这三者背后是否有普遍认可的理论?

5个回答

这些答案,包括所选择的答案,是良好的概念引入的承诺,但在细节缺乏究竟差别在使用图书馆实现它们(还有时出现的术语 重要的区别)。

由于它仍然是一个不断发展的规范,因此目前的答案来自尝试调查参考资料(如wikipedia)和实现(如jQuery):

  • Deferred:从未在流行参考文献1 2 3 4 中描述, 但通常被实现用作承诺解析的仲裁者(实现)。 5 6 7 resolvereject

    有时 deferred 也是承诺(实现then), 5 6 其他时候,让 Deferred 只能够解析并强制用户访问使用承诺被认为更纯粹7 then

  • 承诺:所讨论的战略最全面的词。

    一个代理对象存储我们想要抽象其同步性的目标函数的结果,并暴露一个then接受另一个目标函数并返回一个新承诺的函数。 2

    来自CommonJS 的示例

    > asyncComputeTheAnswerToEverything()
        .then(addTwo)
        .then(printResult);
    44
    

     

    总是在流行的参考文献中描述,尽管从未指定谁的责任解决方案。 1 2 3 4

    始终出现在流行的实现中,并且从未给出解析能力。 5 6 7

  • Future:在一些流行的参考文献1 和至少一个流行的实现中 发现的看似已弃用的术语 8, 但似乎已逐渐退出讨论,而倾向于使用术语“承诺” 3, 并且在该主题的流行介绍中并不总是提及。 9

    然而,至少有一个库使用该术语来概括同步性和错误处理,但不提供then功能。 10 目前尚不清楚避免使用“承诺”一词是否是有意的,但可能是一个不错的选择,因为承诺是围绕“thenables”构建的。 2

参考

  1. 关于承诺和期货的维基百科
  2. 承诺/A+ 规格
  3. 关于 Promise 的 DOM 标准
  4. DOM 标准承诺规范 WIP
  5. DOJO 工具包延期
  6. jQuery 延迟
  7. FutureJS
  8. 关于 Promises 的功能性 Javascript 部分
  9. AngularJS 集成测试中的未来

杂项可能令人困惑的事情

对术语“未来”进行更多说明——期货在许多编程语言中有着悠久的历史,可以追溯到 80 年代中期。该术语今天仍在广泛使用,尤其是在 JVM 上。JavaScript 似乎选择使用术语“Promise”来表示类似于 Java 中“未来”的含义。Scala 将同一概念分为“Future”和“Promise”,分别指代 JavaScript 程序员称为 Promise 的“读取”句柄和“写入”句柄。
2021-03-12 23:14:36
当然,微软必须为它想出自己的术语,所以在 C# 中,它们被称为 Task
2021-03-23 23:14:36

鉴于我显然不喜欢我试图回答 OP 的问题。字面的答案是,promise 是与其他对象共享的东西,而 deferred 应该保密。首先,延迟(通常扩展 Promise)可以自行解决,而 Promise 可能无法这样做。

如果您对细节感兴趣,请查看Promises/A+


据我所知,首要目的是通过标准化接口提高清晰度和松散耦合。请参阅@jfriend00 的建议阅读

不是直接将回调传递给函数,这会导致紧密耦合的接口,使用 Promise 允许人们分离同步或异步代码的关注点。

就我个人而言,我发现 deferred 在处理例如由异步请求填充的模板、加载具有依赖网络的脚本以及以非阻塞方式提供用户反馈以形成数据时特别有用。

确实,比较一下在 JS 模式下异步加载 CodeMirror 之后做某事的纯回调形式(抱歉,我已经有一段时间没有使用 jQuery 了):

/* assume getScript has signature like: function (path, callback, context) 
   and listens to onload && onreadystatechange */
$(function () {
   getScript('path/to/CodeMirror', getJSMode);

   // onreadystate is not reliable for callback args.
   function getJSMode() {
       getScript('path/to/CodeMirror/mode/javascript/javascript.js', 
           ourAwesomeScript);
   };

   function ourAwesomeScript() {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   };
});

对于承诺制定的版本(再次道歉,我不是最新的 jQuery):

/* Assume getScript returns a promise object */
$(function () {
   $.when(
       getScript('path/to/CodeMirror'),
       getScript('path/to/CodeMirror/mode/javascript/javascript.js')
   ).then(function () {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   });
});

为半伪代码道歉,但我希望它使核心思想有些清晰。基本上,通过返回标准化的承诺,您可以传递承诺,从而允许更清晰的分组。

尽管+1我可以知道回调地狱是什么,但这并没有回答这个问题。
2021-03-12 23:14:36
@MartinKällman 你说得对!我有一段时间没有重新审视这个,并且学到了一些东西。我将在下面发布一个单独的答案,但留下这个,因为人们似乎已经从使用示例中受益。
2021-03-15 23:14:36
这是完全错误的,您的示例与回调一样容易。Promises 不是关于回调聚合和解耦,而是提供一个 DSL 来编写异步代码,就像编写同步代码一样。特别fn(callback, errback)是没有任何更紧密耦合或更少用处fn().then(callback, errback)- 但无论如何这是使用承诺的错误方式。我特别讨厌cargo cult 的$.when例子——你绝对没有理由不能拥有一个$.when与回调一起工作函数。
2021-03-23 23:14:36
@MartinKällman,考虑写一个新答案。但是,我认为 OP 实际上想知道 Promises 和 Deferreds 的用途。他的实际问题的答案大致是“延迟可以自行解决。AFAIK承诺和延迟背后的理论来自 [Functional Reactive Programming| haskell.org/haskellwiki/Functional_Reactive_Programming],这是一种扁平化回调的技术.”
2021-03-26 23:14:36
虽然这个答案可能有用,但它并没有真正解决这个问题:所谓的延期是期货或承诺,具体取决于实现。
2021-04-05 23:14:36

真正让我大吃一惊的是 Domenic Denicola 的这个演讲

在一个github gist中,他给出了我最喜欢的描述,非常简洁:

promises 的重点是让我们返回异步世界中的函数组合和错误冒泡。

换句话说,承诺是一种方式,让我们编写异步代码,几乎是一样容易写,如果它是同步的

考虑这个带有承诺的例子:

getTweetsFor("domenic") // promise-returning async function
    .then(function (tweets) {
        var shortUrls = parseTweetsForUrls(tweets);
        var mostRecentShortUrl = shortUrls[0];
        return expandUrlUsingTwitterApi(mostRecentShortUrl); // promise-returning async function
    })
    .then(doHttpRequest) // promise-returning async function
    .then(
        function (responseBody) {
            console.log("Most recent link text:", responseBody);
        },
        function (error) {
            console.error("Error with the twitterverse:", error);
        }
    );

它就像您正在编写此同步代码一样工作:

try {
    var tweets = getTweetsFor("domenic"); // blocking
    var shortUrls = parseTweetsForUrls(tweets);
    var mostRecentShortUrl = shortUrls[0];
    var responseBody = doHttpRequest(expandUrlUsingTwitterApi(mostRecentShortUrl)); // blocking x 2
    console.log("Most recent link text:", responseBody);
} catch (error) {
    console.error("Error with the twitterverse: ", error);
}

(如果这听起来仍然很复杂,请观看该演示文稿!)

关于 Deferred,它是一种方式.resolve().reject()承诺。Promises/B规范中,它被称为.defer(). 在 jQuery 中,它是$.Deferred().

请注意,据我所知,jQuery 中的 Promise 实现已损坏(请参阅该要点),至少从 jQuery 1.8.2 开始。
它应该实现Promises/A thenables,但是您没有得到正确的错误处理,因为整个“异步 try/catch”功能将不起作用。这很遗憾,因为使用异步代码进行“try/catch”非常酷。

如果您打算使用 Promises(您应该用您自己的代码尝试它们!),请使用Kris Kowal 的 QjQuery 版本只是一些回调聚合器,用于编写更清晰的 jQuery 代码,但没有抓住重点。

关于 Future,我不知道,我还没有在任何 API 中看到过。

编辑: 多梅尼克·丹尼科拉 (Domenic Denicola) 在 youtube 上关于承诺的谈话来自@Farm下面的评论。

视频中迈克尔·杰克逊(是的,迈克尔·杰克逊)的引述

我想让你把这句话铭记在心: A promise is an asynchronous value

这是一个很好的描述:promise 就像来自未来的变量 - 对在某些时候将存在(或发生)的事物的一流引用。

Domenic Denicola 在 youtube 上关于 Promises 的演讲:youtube.com/watch? v=hf1T_AONQJU
2021-03-11 23:14:36
这个答案需要更多的赞成票。它应该投票高于 IMO 接受的答案。
2021-03-12 23:14:36
@农场很棒!我会将其添加到答案中。
2021-03-13 23:14:36
@oligofren 感谢您的链接,这看起来不错!顺便说一句,这是一个多么神秘烦人的网站图标,哈哈。
2021-04-02 23:14:36
W3 和 Chrome 核心团队的成员对 Futures(现在在 DOM 中实现!)的一个很好的解释可以在这里找到:xanthir.com/b4PY0
2021-04-07 23:14:36

一个承诺代表某个值的代理创建承诺时未必知道。它允许您将处理程序与异步操作的最终成功值或失败原因相关联。这让异步方法可以像同步方法一样返回值:异步方法返回的不是最终值,而是在未来某个时间点具有值的承诺。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

deferred.promise()方法允许异步函数防止其他代码干扰其内部请求的进度或状态。Promise 仅公开附加处理程序或确定状态(then、done、fail、always、pipe、progress、state 和 promise所需的 Deferred 方法,而不公开更改状态的方法(resolve、reject、notify、resolveWith、 rejectWith 和 notifyWith )。

如果提供了目标,deferred.promise()则将方法附加到其上,然后返回此对象而不是创建一个新对象。这对于将 Promise 行为附加到已经存在的对象很有用。

如果您正在创建一个 Deferred,请保留对 Deferred 的引用,以便在某个时候可以解决或拒绝它。通过 deferred.promise() 仅返回 Promise 对象,以便其他代码可以注册回调或检查当前状态。

简单地说,一个Promise代表一个未知的值,而一个Deferred代表尚未完成的工作。


在此处输入图片说明

图表表示加 1。布拉维西莫!!^_^
2021-03-25 23:14:36
  • Apromise代表一个未知的值
  • Adeferred代表尚未完成的工作

promise 是最初未知的结果的占位符,而 deferred 表示产生该值的计算。

参考