2015-09-02 3 views
38

Я хочу запустить одно и то же действие, независимо от того, успешно разрешено ли мое обещание или нет. Я не хочу связывать одну и ту же функцию с обоими аргументами .then. Разве нет .always, как у jQuery? Если нет, как мне это достичь?ES6 обещание установленный обратный вызов?

+1

вы не можете сделать .finally (функция() {...})? –

+0

См. Http://stackoverflow.com/questions/26667598/will-javascript-es6-promise-support-done-api –

+0

@CharlieWynn Это не определено в Babel и не указано в [MDN] (https: //developer.mozilla. орг/EN-US/Docs/Web/JavaScript/Справка/Global_Objects/Promise/улова). – mpen

ответ

28

Нет ли .always, как у jQuery?

No, there's not (yet). Хотя есть active proposal, возможно, 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) 
    ); 
}; 

И гарантировать, что оригинальное разрешение поддерживается (когда в обратном вызове нет исключения), и что обещания ожидаются.

+2

Я думаю, что обратный вызов ошибки' this.then' в pollyfill должен бросить, а не вернуться. – dfsq

+0

@dfsq: Он делает, возвращая оригинал, отклонил обещание :-) – Bergi

+0

Вы правы, 'this' является оригинальным обещанием из-за' => ', конечно. – dfsq

6

С асинхронной/Await, вы можете сочетание await с try/finally, например, так:

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

Вот реальный пример, который я запущен в производство с узлом, используя 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 ИНГ на первом Promise- это деталь реализации Sequelize Он использует CLS для «привязки» транзакции SQL к Promise цепи Все это приводит к тому, что внутри та же цепочка привязана к транзакции. Ничего снаружи нет. Поэтому ожидание обещания «закрыло» блок транзакций и сломал цепочку. Я бросил этот пример, чтобы показать вам, как «ваниль» «Совместимость с обещаниями может быть смешана вместе с асинхронными функциями и хорошо работать вместе.)

+1

Почему вы не используете транзакцию async-функции и не вызываете вызов 'then'? – Bergi

+1

Уход! Никогда не думал об использовании 'try/finally' с' async/await'. Это пригодится. – mpen

+0

@Bergi - Sequelize использует что-то, называемое CLS, чтобы охватить блок транзакций цепочкой Promise. Если я использовал 'await', он вернул обработчик транзакции, но последующий SQL был бы _outside_ блока и, следовательно, не привязан к транзакции. Это деталь реализации Sequelize. –

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 
}); 
+2

Я не думаю, что это то же самое. См. Здесь [3-й и 4-й пункты] (https://github.com/tc39/proposal-promise-finally#why-not-thenf-f). Мы можем фактически использовать решение Берги без изменения прототипа. Вам просто нужно называть это смешным: 'finallyFunc.call (thePromise, callback)'. Хорошо работает с оператором bind. – mpen

+0

@mpen, ты прав. Отредактировано с исправлением. – user2426679

1

Вот моя реализация .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 
0

Нет необходимости вводить новые понятия

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

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

Это приведет к возврату возвращаемого значения из первой ветки .then(), не так ли? – thenickdude

+0

Это правильно. –

0

Продлить Bergi ответ.

Возврат Promise.reject() в обработчике улова предотвратит финализацию «then» для вызова.

Так что, если вы собираетесь обрабатывать ошибки обещания 2+ раз вы должны использовать шаблонный так:

return myPromise() 
.then(() => ...) 
.catch((error) => { 
    ... 
    myFinnaly(); 
    return Promise.reject(error); 
}) 
.then(() => myFinnaly()); 
Смежные вопросы