【问题标题】:How to execute code at the very end of a promise chain in q如何在 q 中的 Promise 链的最后执行代码
【发布时间】:2014-11-16 12:20:57
【问题描述】:

假设你有:

function setTimeoutPromise(ms) {
  var defer = Q.defer();
  setTimeout(defer.resolve, ms);
  return defer.promise;
}

然后你有类似的东西:

function foo(ms) {
  return setTimeoutPromise(ms).then(function () {
    console.log('foo');
  });
}

function afterItAll() {
  console.log('after it all');
}

foo(100).then(function () {
  console.log('after foo');
}).then(afterItAll);

有没有办法修改 foo 以便 afterItAllafter foo 块之后执行?例如类似:

function foo(ms) {
  return setTimeoutPromise(ms).then(function () {
    console.log('foo');
  }).SOMEPROMISECONSTRUCT(function () {
    console.log('after it all');
  });
}

foo(100).then(function () {
  console.log('after foo');
});

我问的原因是我正在开发一个 API,用户将在其中进行几次这样的 foo 调用,如果 after foo em> 代码在这些 API 调用后自动执行。我知道我可以使用回调来完成此操作,但我真的很想坚持使用 Promise。

【问题讨论】:

标签: javascript promise q chaining


【解决方案1】:

不,没有。

好吧,让我们看看你在这里问什么:

有没有办法修改 foo 以便 afterItAll 在 after foo 块之后执行?

这实际上是在问:

有什么方法可以知道什么时候没有更多的 .then 处理程序将添加到特定的承诺中?

其中,给定一个任意函数,我们可以决定添加一个 fooResult.then(function(){}) 作为程序中的最后一件事,然后我们 return 来自它,所以这就像在问:

有什么方法可以知道函数何时/是否返回?

将整个程序作为一个函数,这就像在问:

有什么方法可以知道程序是否会停止?

It's not an easy thing to do 至少可以这么说。这个功能不仅不存在,理论上也是不可能的。

那我该如何处理呢?

Bergi's answer 给了你一个很好的主意。我们在 Bluebird 中为之奋斗的核心是嵌套。

因为我们想要的东西在一般情况下是不可能的,所以我们必须反转控制,就像回调一样:

function transaction(fn){
    // Promise.resolve().then( in ES6/Bluebird promises
    return Q().then(function(){ 
        return fn()
    }).finally(function(){ // same in Bluebird, in ES6 that's `.then(fn, fn)`
        console.log("after it all!");
    })
}

这会让你这样做:

transaction(function(){
    return setTimeoutPromise().then(more).then(more);
});

哪个会运行 setTimeoutPromise,然后是 more,然后是另一个,并且在两者都完成后将“全部”记录。这种模式对于 DB 驱动程序和资源获取非常有用。

【讨论】:

    【解决方案2】:

    不,没有这样的承诺结构。 Promise 不知道它是否是链的末端,或者其他代码是否会附加另一个链接。

    然而,结合 Promise 和回调代码并没有错:

    function foo(ms, within) {
      return setTimeoutPromise(ms).then(function () {
        console.log('foo');
      })
      .then(within)
      .then(function afterFooAll() { // possibly use `finally` here?
        console.log('cleanup');
      });
    }
    
    foo(100, function () {
      console.log('inside foo');
    }) // now returns a promise for cleanup been done
    

    我不确定您的实际用例是什么,但您可能还想看看Bluebird's Promise.using 资源管理模式。

    【讨论】:

      【解决方案3】:

      好的!希望我的经验对你有所帮助。

      我遇到了非常相似的问题。

      我希望在执行完所有 sql 语句后释放 mysql 连接池, 或失败...如下所示

      getConnection
      .then(exeuteSQL('select ...'))
      .then(exeuteSQL('select ...'))
      .then(null, function(err){ //err handling })
      ....
      .. finally execute pool release
      

      这可以通过像这样的.done()方法来完成

      getConnection
      .then(exeuteSQL('select ...'))
      .then(exeuteSQL('select ...'))
      .then(null, function(err){ //err handling })
      ....
      .done(function(){
          console.log('this line always executed!');
          req.conn.release();   // assuming connection attached to request object before
      })
      

      PS) 我的应用环境是 node.js 并使用 'q' promise 模块

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-08
        • 2017-02-20
        • 2017-09-20
        相关资源
        最近更新 更多