【问题标题】:Convert a method with a callback to one that returns a promise with cleanup将带有回调的方法转换为返回带有清理的 Promise 的方法
【发布时间】:2015-12-02 14:53:56
【问题描述】:

我正在尝试编写一个函数来执行异步任务并返回一个承诺,同时确保在任何回调完成后进行清理。但是要做到这一点,我似乎需要提前知道回调,这样我才能确保它在清理发生之前发生。

目前,该函数的一般结构如下所示:

function doSomethingWithResourceAsync(someParameter, usePreparedResourceCb) {
    var resource = acquireResource(someParameter);
    return prepareResourceAsync(resource)
        .then(usePreparedResourceCb)
        .finally(doCleanup);

    function doCleanup() {
        releaseResource(resource);
    }
}

要调用它,我会这样做:

doSomethingWithResourceAsync(myParameter, myCallback)
    .then(andSoOn);

function myCallback(proxyObj) {
    return doMagicAsync(proxyObj);
}

这是我让它工作的唯一方法。

但是,我想以一种可以链接我的回调而不需要传递清理回调的方式来编写它。所以我想这样称呼它:

function doSomethingWithResourceHopefullyAsync(myParameter) {
    var resource = acquireResource(someParameter);
    return prepareResourceAsync(resource)
        .finally(doCleanup); // uh oh

    function doCleanup() {
        releaseResource(resource);
    }
}

doSomethingWithResourceHopefullyAsync(myParameter)
    .then(myCallback) // too bad, already cleaned up
    .then(andSoOn);

但这不起作用,因为清理工作发生在 myCallback 获得控制权之前。

如果可能,我应该如何构建我的方法来实现我的目标?或者我能在这种情况下做些什么?

我觉得我可以使用延迟来实现我的目标,但我不知道如何设置它来完成这项工作。

我正在尝试开发的 API 是供那些不一定了解异步方法复杂性的用户使用的,所以我想尽可能地隐藏它。

【问题讨论】:

  • 用 Bluebird 代替 Q,你可以使用 using: github.com/petkaantonov/bluebird/blob/master/…
  • 这在未来可能会非常有用,感谢您指出这一点。
  • 很高兴自己弄清楚这个模式:)
  • 您能否将您的示例扩展为需要清理的内容,以及它需要如何等待callback 创建的承诺(或者:如果您清理了callback 中的哪些内容将不起作用过早上升)?有一种模式可以解决这个问题,但我需要详细说明您的问题。
  • @Bergi:这是 Node 应用程序的一部分。在某些情况下,它只是用于记录。其他实例,它用于启动子进程(和日志记录)。在我的用例中,必须运行子进程才能使回调起作用。有机会我会更新的。

标签: javascript callback promise q


【解决方案1】:

您拥有的是disposer pattern。自己解决的道具:)

“传递”回调是必要的,因为它创建了一个作用域,它可以有效地进行清理。你会知道回调是如何通过返回一个承诺来“完成”的。您需要一个范围的事实是基本的,因为它很好……范围清理。通过范围 (RAII) 将资源分配绑定到实例化是一种有用的技术。

我会这样做:

function withResource(handler){
    return acquireResource(). // important to return to chain, like your code
           then(handler).finally(cleanup);
}

这实际上是你已经拥有的。

正如 cmets 所建议的,bluebird 的 using 是一个非常有用的抽象,它返回的处理程序为您提供了强大的清理功能并清理了很多类型错误。我强烈推荐它(尽管我显然有偏见)。

【讨论】:

  • 当我提到传递回调时,我指的不是处理程序(在我的示例中是 cb() 函数),而是 doCleanup() 函数。我不能完全相信所有调用代码都会记得总是在处理程序中调用清理函数。但很高兴看到你同意我在这里的决定。回想起来,我认为这确实是最干净的方法。实际函数的名称已经暗示它将从头到尾做某事。说“完成此操作,然后执行此操作”并期望它起作用是没有意义的。
  • 确切地说,您必须以某种方式定义“开始”和“结束”,您最好使用范围来完成它,这意味着您永远不会忘记自己进行清理。这是许多编程语言的基础,有趣的是看到语言在其核心中集成了该工具——比如 C# 的 async using 提案和 Python 3.5 的 async with。在 JavaScript 中进行这种资源管理会很棒,但遗憾的是,我真的不认为我们还在那里 - 当 async/await 明年(希望)登陆时,我们将拥有更多令人信服的力量:)
猜你喜欢
  • 2020-10-19
  • 2012-09-19
  • 2014-10-30
  • 2016-01-18
  • 1970-01-01
  • 1970-01-01
  • 2014-04-30
  • 2015-10-14
  • 2017-06-11
相关资源
最近更新 更多