web-dev-qa-db-ja.com

ES6は解決済みのコールバックを約束しますか?

Promiseが正常に解決されたかどうかに関係なく、同じアクションを実行したいと思います。同じ関数を.thenの両方の引数にバインドしたくありません。 jQueryのような.alwaysはありませんか?そうでない場合、どうすればこれを達成できますか?

43
mpen

.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)
    );
};

どちらも、元の解決が維持され(コールバックに例外がない場合)、プロミスが待機することを保証します。

34
Bergi

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でawaitingしないのか疑問に思われる場合は、Sequelizeの実装の詳細です。 [〜#〜] cls [〜#〜] を使用して、SQLトランザクションをPromiseチェーンに「バインド」します。発生するもの内部同じチェーンのスコープはトランザクションに限定されます。外部のものは対象外です。したがって、Promiseで待機すると、トランザクションブロックが「閉じ」、チェーンが壊れます。非同期関数、および一緒によく遊ぶ。)

9
Lee Benson

プロトタイプを更新できない場合は、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
user2426679

これが.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
1
Chong Lip Phang

概要:

これでPromise.prototype.finally()にもアクセスできるようになりました。これは、いくつかのクリーンアップを実行する最後の要素としてpromiseチェーンに配置できる関数です。 Promise.thenおよびPromise.catchと比較すると、次のように機能します。

  • Promise.thenonly promiseが解決されたときに呼び出されます(最初の引数のコールバック関数のみを指定した場合)
  • Promise.catchonly promiseが拒否されたときに呼び出されます
  • Promise.finallyalwaysは、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がこの問題を解決してくれます。

1

Bergiの答えを拡張する。

キャッチハンドラーでPromise.reject()を返すと、 'then'のファイナライズが呼び出されなくなります。

したがって、プロミスエラーを2回以上処理する場合は、次のようなボイラープレートを使用する必要があります。

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

新しい概念を導入する必要はありません

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

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