【问题标题】:When would someone need to create a deferred?什么时候需要创建延迟?
【发布时间】:2015-12-27 11:20:43
【问题描述】:

现在通常不鼓励创建延迟对象,而倾向于使用 ES6 风格的 Promise 构造函数。是否存在有必要(或以某种方式更好)使用延迟的情况?

例如,在this page 上,给出以下示例作为使用延迟的理由:

function delay(ms) {
    var deferred = Promise.pending();
    setTimeout(function(){
        deferred.resolve();
    }, ms);
    return deferred.promise;
}

但是,这也可以使用 Promise 构造函数来完成:

function delay(ms) {
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve();
        }, ms);
    });
}

【问题讨论】:

  • 使用 promise 构造函数的情况也非常少见。物有所值。
  • 正如 Benjamin Gruenbaum 所说,您甚至很少需要使用 new Promise。你上面的例子就是一个很好的例子。解析为undefined 的延迟承诺不是很有用。您通常希望延迟现有的承诺,例如somPromise.delay(t).then(...),在这种情况下,您的延迟不会产生新的承诺。如果你需要在没有 Promise 的地方启动 Promise 链,你可以简单地使用 Promise.resolvePromise.reject
  • function() { resolve(); } 在这种情况下等同于 resolve
  • @caasjj 对于某些 Promise 实现确实如此,但并非所有实现都提供 .delay()-type 方法。 ES6 原生 Promise 肯定不会。

标签: javascript promise ecmascript-6 bluebird es6-promise


【解决方案1】:

是否存在需要(或只是 更好的方式)使用延迟?

不存在需要延期的情况。 “更好”是一个见仁见智的问题,所以我不会在这里讨论。

ES6 Promise 规范没有延迟对象是有原因的。 你根本不需要。人们过去使用延迟对象的任何事情都可以总是以不使用延迟对象的另一种方式完成。

首先,大多数用途都非常适合 Promise 构造函数模型。其次,任何其他不完全适合该模型的情况仍然可以使用 Promise 构造函数或从已解决的 Promise 开始并链接到它来完成。

我看到的 deferred 的主要用例是,当您想将resolve()reject() 的功能传递给创建promise 的代码以外的其他一些代码时。 Deferred 使这变得非常容易,因为您可以只传递延迟对象,并且它具有解决或拒绝它的公共方法。但是,通过承诺,您也可以只传递 resolve 和/或 reject 方法。由于它们自动绑定到特定对象,因此您只需传递函数引用即可。而且,在其他情况下,您可以让其他代码创建自己的承诺并自己解决/拒绝它,并将该操作链接到您的操作,而不是让他们解决/拒绝您的承诺。这一切都和传递一个延迟对象一样干净吗?主要是见仁见智,但都不是很常见的用例,所有这些都可以在没有单独的延迟对象的情况下完成。

而且,正如 torazaburo 指出的那样,让一些外部代码解决或拒绝您的承诺本身就是一种反模式。您创建了承诺 - 您解决/拒绝了它。如果您想使用外部事件来决定何时解决/拒绝它,那么让他们通知您(通过他们自己的承诺或回调),您可以解决/拒绝自己的承诺。或者,让他们创建自己可以使用的承诺。这确实是理想的模型。或者,让他们链接到您的承诺,以便在他们的操作完成时确定最终结果。

如果一个人习惯于使用 deferred 对象进行编码(比如使用 jQuery deferred),可能需要一点时间来适应没有它的编码,但过了一会儿你就会开始以不同的方式思考,它开始变得自然只使用 promise 构造函数。

一旦你承诺了你在任何给定应用程序中使用的异步操作范围,你甚至很少需要再创建自己的 Promise,因为你大多只是在构建你调用的异步函数已经存在的 Promise创建或使用流控制操作,如Promise.all(),为您创建超级承诺。

这是反模式的要点。 使用已经为您创建的承诺,而不是手动创建更多。链接它们。从.then() 处理程序返回承诺以在逻辑控制下链接异步操作。

当然,存在不返回承诺的现有异步操作,有人需要为其创建承诺,但这应该在您的主要编码逻辑范围之外的某个承诺层中完成,并且只为此完成一次操作,然后调用异步操作的代码应该能够只使用返回的承诺。

【讨论】:

  • 是的,很好的解释。但是关于将resolve()或reject()的能力传递给创建promise的代码以外的其他代码,值得指出的是,这本身就是一种反-图案。承诺构造函数的设计旨在促进将所有承诺逻辑封装在回调中,至少在可能的范围内,这几乎是所有时间。
  • @torazaburo - 我同意。由于我放弃了 jQuery Deferred 对象并且刚刚习惯了一种不同的编码方式,所以我从来没有找到使用或需要这种编码模式的理由。如果您使用外部事物来触发解决您创建的 Promise,您可以随时让他们以其他方式通知您(通过他们自己的 Promise 或通过回调),然后您可以解决您自己的 Promise。但是,我们似乎都不愿明确地说从来没有这样一个有效的案例(我们真的无法证明没有这样的案例),因此我提供了一个解决方法以防万一。
  • @jfriend00 对于尚未创建的 Promise 的 Promise 怎么样?例如,应该在未来某个时间点触发的“固定”网络请求:/* in a function that prepares the request */ var deferred = Promise.defer(); function makeRequest() { doMakeRequest().then(function(data) {deferred.resolve(data)}) .catch(function(e) {deferred.reject(e)}); } queue.push(request); return deferred.promise; ..... /* at some point later in another galaxy */ makeRequest = queue.shift(); makeRequest(); 同步 Promise 构造函数如何实现?
  • @mderk - 如果你描述了一个现实世界的问题,那么我可以试一试如何在不使用延迟的情况下解决它。我个人从来没有遇到过需要承诺的承诺(无法使用链接),所以我不知道现实世界的情况会是什么,因此无法真正为您提供合适的替代方案。我可以为您的理论问题提出一个理论解决方案,但这似乎没有用,因为到目前为止您对代码的描述假设了很多实现,如果整个问题已知,则可能不必这样做.
  • @mderk - 另外,如果您想讨论一个特定问题以及如何在没有延迟的情况下解决它,那么我建议您发布一个新问题并描述您的问题,然后给我留言这会通知我新问题,因为我认为您无法仅在此处的评论中完整描述您要尝试做的事情。
【解决方案2】:

Native Promise 没有 deferred 开箱即用的所有内置方法,例如在其构造函数范围之外解析 Promise 的方法(不过,使用原生 Promise 很容易实现),并且查看 Promise 状态的能力(在原生 Promises 上对常规 JavaScript 隐藏,但可以在开发工具中检查)。

今天使用 deferred 的主要原因是向后兼容依赖于这些额外方法的代码。如果不涉足意见领域,很难对您的问题给出明确的答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-14
    • 2012-09-16
    • 1970-01-01
    • 1970-01-01
    • 2018-12-10
    • 2010-12-29
    • 2017-07-13
    • 2012-08-21
    相关资源
    最近更新 更多