Я хочу запустить одно и то же действие, независимо от того, успешно ли выполнено мое обещание или нет. Я не хочу связывать одну и ту же функцию с обоими аргументами .then
. Разве нет .always
, как jQuery? Если нет, как мне это достичь?
ES6 обещал решить обратный вызов?
Ответ 1
Не существует ли
.always
как jQuery?
Нет, там нет (пока). Хотя есть активное предложение , возможно, 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)
);
};
Оба гарантируют, что исходное разрешение поддерживается (когда в обратном вызове нет исключения) и ожидается, что promises.
Ответ 2
С помощью async/await вы можете сочетать await
с 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();
}
});
}
Я использую этот код внутри теста мокки рядом с Sequelize ORM, чтобы начать транзакцию БД, и независимо от результата вызовов БД внутри тест, всегда откат в конце.
Это примерно аналогично методу Bluebird .finally()
, но IMO, гораздо более приятный синтаксис!
( Примечание. В случае, если вам интересно, почему я не await
на первом обещании - это деталь реализации Sequelize. Он использует CLS "привязать" транзакцию SQL к цепочке Promise. Все, что происходит внутри одной и той же цепочки, привязано к транзакции. Ничего снаружи нет. Поэтому ожидание на Promise будет "закрыл" блок транзакций и сломал цепочку. Я привел этот пример, чтобы показать вам, как 'vanilla' Обработка обещаний может быть смешана вместе с асинхронными функциями и хорошо сочетаться.)
Ответ 3
Если вы не обновляете прототип/не можете, способ взломать наконец-то:
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
});
Ответ 4
Вот моя реализация .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
Ответ 5
Не нужно вводить новые понятия
const promise = new Promise((resolve, reject) => {
/*some code here*/
});
promise.then(() => {
/* execute success code */
}, () => {
/* execute failure code here */
}).then(() => {}, () => {}).then(() => {
/* finally code here */
});
Ответ 6
Чтобы продлить ответ Берги.
Возвращение Promise.reject() в обработчике улова предотвратит финализацию "then" для вызова.
Поэтому, если вы собираетесь обрабатывать обещание с ошибкой 2+, вы должны использовать шаблонный шаблон следующим образом:
return myPromise()
.then(() => ... )
.catch((error) => {
...
myFinnaly();
return Promise.reject(error);
})
.then(() => myFinnaly());