ES6 承诺解决回调?

ES6 promise settled callback?

无论我的 Promise 是否成功解决,我都想 运行 执行相同的操作。我不想将相同的函数绑定到 .then 的两个参数。难道没有像 jQuery 那样的 .always 吗?如果没有,我该如何实现?

Isn't there a .always like jQuery has?

不,there's not (yet). Though there is an active proposal,所以可能是 ES2018。
是的,有:promise .finally() 是自 ES2018 以来标准的一部分。

If not, how do I achieve this?

您可以像这样自己实现 finally 方法:

Promise.prototype.finally = function(cb) {
    const res = () => this
    const fin = () => Promise.resolve(cb()).then(res)
    return this.then(fin, fin);
};

或更广泛地,将解析信息传递给回调:

Promise.prototype.finally = function(cb) {
    const res = () => this
    return this.then(value =>
        Promise.resolve(cb({state:"fulfilled", value})).then(res)
    , reason =>
        Promise.resolve(cb({state:"rejected", reason})).then(res)
    );
};

两者都确保维持原始决议(当回调中没有异常时)并等待承诺。

和async/await,你可以组合awaittry/finally,像这样:

async function(somePromise) {
  try {
    await somePromise();
  } finally {
    // always run this-- even if `somePromise` threw something
  }
}

这是一个真实的例子,我在 Node 的生产中有 运行,使用 Babel 的 async-to-generator 插件。

// Wrap promisified function in a transaction block
export function transaction(func) {
  return db.sequelize.transaction().then(async t => {
    Sequelize.cls.set('transaction', t);
    try {
      await func();

    } finally {
      await t.rollback();
    }
  });
}

我在 mocha 测试中使用此代码与 Sequelize ORM 一起启动数据库事务,无论测试中数据库调用的结果如何,总是在最后回滚。

这与 Bluebird 的 .finally() 方法大致相似,但在我看来,语法要好得多!

(注意:如果您想知道为什么我不await第一个 Promise - 它是 Sequelize 的一个实现细节。它使用 CLS 到 'bind' 到 Promise 链的 SQL 交易。在 内部 同一链中发生的任何事情都在交易范围内。任何外部都不是. 因此,在 Promise 上等待会 'closed' 交易块并破坏链条。我将这个示例放入其中以向您展示 'vanilla' Promise 处理如何与异步函数混合使用,并一起发挥作用。 )

如果您不t/can不更新原型,破解 finally 的方法是:

executeMyPromise()
.then(function(res){ return {res: res}; })
.catch(function(err){ return {err: err}; })
.then(function(data) {
    // do finally stuff
    if (data.err) {
        throw data.err;
    }
    return data.res;
}).catch(function(err) {
    // handle error
});

这是我对 .finally() 的实现。

Promise.prototype.finally = function(cb) {
   return this.then(v=>Promise.resolve(cb(v)),
                    v=>Promise.reject(cb(v)));
};

我测试过:

(new Promise((resolve,reject)=>{resolve(5);})).finally(x=>console.log(x));  //5

(new Promise((resolve,reject)=>{reject(6);})).finally(x=>console.log(x));  //6

(new Promise((resolve,reject)=>{reject(7);}))
.then(x=>x,y=>y)
.catch(x=>{throw "error";}) 
.finally(x=>{console.log(x); throw "error"; return x;})  // 7
.then(x=>console.log(x),y=>console.log('e'));  //e
// Uncaught (in promise) undefined

无需引入新概念

const promise = new Promise((resolve, reject) => {
  /*some code here*/
});

promise.then(() => {
  /* execute success code */
}, () => {
  /* execute failure code here */
}).then(() => {}, () => {}).then(() => {
  /* finally code here */
});

扩展答案。

在捕获处理程序中返回 Promise.reject() 将阻止最终化 'then' 被调用。

所以如果你要处理 promise 错误 2 次以上,你应该像这样使用样板:

return myPromise()
.then(() => ... )
.catch((error) => {
  ...
  myFinnaly();
  return Promise.reject(error);
})
.then(() => myFinnaly());

总结:

我们现在也可以访问 Promise.prototype.finally()。这是一个可以作为执行某些清理的最后一个元素放在承诺链上的函数。与 Promise.thenPromise.catch 相比,它的工作方式如下:

  • Promise.then 只有在承诺被解决时被调用(如果你只把它放在第一个参数回调函数)
  • Promise.catch 只有 在承诺被拒绝时被调用
  • Promise.finally 总是 在 promise 完成时被调用,所以当 promise 被拒绝或解决时。

示例:

let Prom = new Promise((res, rej) => {
  let random = Math.random();
  
  if (random > 0.5) {
    res(1);
  } else {
    rej('Error occured')
  }

  
});


Prom.then((val) => {
  console.log(val);
  return val * 10;
}).catch((err) => {
  console.log(err);
}).finally(() => {
  console.log('finally executed');
})

在上面的例子中,我们可以观察到 finally 总是被执行,无论 promise 是 resolve 还是 reject。并不是说 finally 理想情况下应该总是在 promise 链的末尾进行一些清理,无论 Promise 结果如何都应该执行。

使用 finally 的优点是它避免了代码重复的需要,因为它是针对已解决和已拒绝的承诺执行的。否则我们将不得不使用像这样的技巧:

.then(onfullfilled, onfullfilled)

.then(onfullfilled)
.catch(onfullfilled)

请注意,现在我们必须将 onfullfilled 函数定义为 promisehandler 本身之外的命名函数(或者传入 2 个匿名函数副本,这更不优雅)。 Promise.finally为我们解决了这个问题。

allSettled 直接作为 finally 工作:

Promise.allSettled([promiseSuccess, promiseReject])
  .then(results => console.log);

检查:https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled