ES6 Promise结算回调?

IT技术 javascript ecmascript-6 es6-promise
2021-02-05 00:05:15

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

6个回答

难道没有.always像 jQuery 那样的吗?

不,没有(还)虽然有一个积极的提案,所以也许是 ES2018。
是的,有:promise.finally()是自 ES2018 以来标准的一部分。

如果没有,我该如何实现?

您可以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)
    );
};

两者都确保保持原始分辨率(当回调中没有异常时)并等待Promise。

您的第一个实现(没有值)似乎符合规范,所以很好:-)
2021-03-18 00:05:15
@dfsq:它通过返回原始的、被拒绝的Promise来实现:-)
2021-03-21 00:05:15
我认为this.thenin pollyfill 的错误回调应该抛出而不是返回。
2021-04-02 00:05:15
您的第一个示例与此处描述的问题相同,不是吗?不过,我不确定您的“更广泛”的解决方案。
2021-04-08 00:05:15
你说得对,this是因为=>,肯定是当初的诺言
2021-04-11 00:05:15

使用 async/await,您可以将awaitwith组合起来try/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()方法,但是 IMO,语法要好得多!

:如果你想知道为什么我不是await第一个Promise-荷兰国际集团是Sequelize的实现细节它使用。CLS有什么要“绑定” SQL事务的无极链招致。里面同样链范围限定于事务。外部的任何东西都不是。因此,在 Promise 上等待将“关闭”事务块并破坏链。我把这个例子放在向你展示“普通”Promise 处理如何与异步函数混合使用,并一起玩得很好。)

整洁的!从未想过使用try/finallywith async/await那会派上用场的。
2021-03-15 00:05:15
你为什么不使用async function transaction并挂断那个then电话?
2021-04-06 00:05:15
@Bergi - Sequelize 使用称为“CLS”的东西将事务块的范围限定为 Promise 链。如果我使用await,它将返回一个事务处理程序,但随后的 SQL 将在块之外,因此不限于事务。这是 Sequelize 的一个实现细节。
2021-04-06 00:05:15

如果您不/不能更新原型,则破解 a 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
});
我不认为那是一样的。请参阅此处第 3 点和第 4 点我们实际上可以在不修改原型的情况下使用Bergi 的解决方案。你只需要称之为有趣:finallyFunc.call(thePromise, callback)与绑定运算符一起使用效果很好。
2021-04-03 00:05:15

这是我对 .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

概括:

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

  • Promise.then 在Promise解决时才被调用(如果你只把它作为第一个参数回调函数)
  • Promise.catch 只有在Promise被拒绝时才会被调用
  • 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还是rejects 它总是被执行。finally理想情况下,这不应该总是在 Promise 链的末尾进行一些清理,无论 Promise 结果如何,都应该执行这些清理。

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

.then(onfullfilled, onfullfilled)

或者

.then(onfullfilled)
.catch(onfullfilled)

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