Promiseが正常に解決されたかどうかに関係なく、同じアクションを実行したいと思います。同じ関数を.then
の両方の引数にバインドしたくありません。 jQueryのような.always
はありませんか?そうでない場合、どうすればこれを達成できますか?
.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)
);
};
どちらも、元の解決が維持され(コールバックに例外がない場合)、プロミスが待機することを保証します。
Async/awaitを使用すると、次のようにawait
と_try/finally
_を組み合わせることができます。
_async function(somePromise) {
try {
await somePromise();
} finally {
// always run this-- even if `somePromise` threw something
}
}
_
以下は、Babelの async-to-generator プラグインを使用してNodeで本番環境で実行している実際の例です。
_// 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 と一緒に使用してDBトランザクションを開始し、テスト内のDB呼び出しの結果に関係なく、常に最後にロールバックします。
これはBluebirdの.finally()
メソッドとほぼ同じですが、IMOの方がはるかに優れた構文です!
(注:なぜ私が最初のPromiseでawait
ingしないのか疑問に思われる場合は、Sequelizeの実装の詳細です。 [〜#〜] cls [〜#〜] を使用して、SQLトランザクションをPromiseチェーンに「バインド」します。発生するもの内部同じチェーンのスコープはトランザクションに限定されます。外部のものは対象外です。したがって、Promiseで待機すると、トランザクションブロックが「閉じ」、チェーンが壊れます。非同期関数、および一緒によく遊ぶ。)
プロトタイプを更新できない場合は、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
これでPromise.prototype.finally()
にもアクセスできるようになりました。これは、いくつかのクリーンアップを実行する最後の要素としてpromiseチェーンに配置できる関数です。 Promise.then
およびPromise.catch
と比較すると、次のように機能します。
Promise.then
only promiseが解決されたときに呼び出されます(最初の引数のコールバック関数のみを指定した場合)Promise.catch
only promiseが拒否されたときに呼び出されますPromise.finally
alwaysは、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');
})
上記の例では、promiseが解決するか拒否するかに関係なく、finally
が常に実行されることがわかります。 finally
は、Promiseの結果に関係なく実行する必要があるクリーンアップを実行するために、常にプロミスチェーンの最後にあるのが理想的ではありません。
finally
を使用する利点は、解決されたプロミスと拒否されたプロミスの両方に対して実行されるため、コードの重複が不要になることです。それ以外の場合は、次のようなハックを使用する必要があります。
.then(onfullfilled, onfullfilled)
または
.then(onfullfilled)
.catch(onfullfilled)
onfullfilled
関数をpromisehandler自体の外の名前付き関数として定義する必要があることに注意してください(または2つの匿名関数のコピーを渡すと、エレガントさがさらに低下します)。 Promise.finally
がこの問題を解決してくれます。
Bergiの答えを拡張する。
キャッチハンドラーでPromise.reject()を返すと、 'then'のファイナライズが呼び出されなくなります。
したがって、プロミスエラーを2回以上処理する場合は、次のようなボイラープレートを使用する必要があります。
return myPromise()
.then(() => ... )
.catch((error) => {
...
myFinnaly();
return Promise.reject(error);
})
.then(() => myFinnaly());
新しい概念を導入する必要はありません
const promise = new Promise((resolve, reject) => {
/*some code here*/
});
promise.then(() => {
/* execute success code */
}, () => {
/* execute failure code here */
}).then(() => {}, () => {}).then(() => {
/* finally code here */
});