2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Promiseのメモ - その2: チェーン、例外

Last updated at Posted at 2019-10-08

Promiseのメモ - その1: resolveとthen の続き。

チェーン

thenやcatchをいくつもつないで、「1つめの処理→2つめの処理」のような流れを作ることをプロミスチェーンと言います。

次の例は、2つめのthenを追加したものです。1つめのthenのコールバック関数で戻り値を返すと、その戻り値が2つめのthenのコールバック関数の引数になります。

function makePromise(num) {
  return new Promise((resolve, reject) => {
    if(num % 2 == 0)
      resolve('OK');
    else
      reject('NG');
  });
}

makePromise(2).then((x) => {
  console.log(`then1 callback: ${x}`);
  return x + x;
}).then((x) => {
  console.log(`then2 callback: ${x}`);
}).catch((r) => {
  console.log(`catch callback: ${r}`);
});
結果
then1 callback: OK
then2 callback: OKOK

makePromise(1)としてrejectすると、thenは2つともスキップされて、catchに渡した関数が実行されるように見えます。

結果
catch callback: NG

thenとcatchが返すPromise

MDNの説明でthenの仕様をチェックします。

The then() method returns a Promise.

thenはPromiseオブジェクトを返します。はっきり書いていませんが、新しいPromiseオブジェクトを作って返すということです。

  • returns a value, the promise returned by then gets resolved with the returned value as its value;
  • doesn't return anything, the promise returned by then gets resolved with an undefined value;

コールバック関数が実行されると、新しいPromiseオブジェクトは履行済みになり、履行済み結果の値はコールバック関数の戻り値になります。戻り値がないときは、履行済み結果の値はundefinedになります。

次のようにチェーンをばらしてthenが返すPromiseを調べます。また、コールバック関数が実行されたあとの状態も調べます。

let promise = makePromise(2);
console.log(promise);
let promise_then1 = promise.then((x) => {
  console.log(`then1 callback: ${x}`);
  return x + x;
});
console.log(promise_then1);
let promise_then2 = promise_then1.then((x) => {
  console.log(`then2 callback: ${x}`);
});
console.log(promise_then2);
let promise_catch = promise_then2.catch((r) => {
  console.log(`catch callback: ${r}`);
});
console.log(promise_catch);

setTimeout(() => {
  console.log(promise);
  console.log(promise_then1);
  console.log(promise_then2);
  console.log(promise_catch);
}, 1000);
結果(chromeの場合)
Promise {<resolved>: "OK"}
Promise {<pending>}
Promise {<pending>}
Promise {<pending>}
then1 callback: OK
then2 callback: OKOK
Promise {<resolved>: "OK"}
Promise {<resolved>: "OKOK"}
Promise {<resolved>: undefined}
Promise {<resolved>: undefined}

makePromise関数が1つ、thenが2つ、catchが1つのPromiseオブジェクトを作っています。thenとcatchが作ったPromiseは非同期で自動的に保留中から履行済みに変わっています。

makePromise(1)としてrejectすると、次の結果になります。

結果(chromeの場合)
Promise {<rejected>: "NG"}
Promise {<pending>}
Promise {<pending>}
Promise {<pending>}
catch callback: NG
Promise {<rejected>: "NG"}
Promise {<rejected>: "NG"}
Promise {<rejected>: "NG"}
Promise {<resolved>: undefined}

thenやcatchはスキップされるわけでなく、「thenで登録したコールバック関数は棄却済みのときは実行されない」「catchで登録したコールバック関数は履行済みのときは実行されない」ということです。実行されないときは、履行/棄却済み結果の値は次のPromiseオブジェクトに引き継がれます。

catchもPromiseを返すので、catchのうしろにthenをつなげることができます。

makePromise(2).then((x) => {
  console.log(`then1 callback: ${x}`);
  return x + x;
}).catch((r) => {
  console.log(`catch callback: ${r}`);
  return r + r;
}).then((x) => {
  console.log(`then2 callback: ${x}`);
});
結果
then1 callback: OK
then2 callback: OKOK

この例はmakePromise(1)とすると、then2 callback: NGNG となります。

例外

rejectだけでなく、一般的な例外が発生したときも、catchで登録したコールバック関数が呼ばれます。コールバック関数の引数にはErrorオブジェクトが入ります。

function makePromise(num) {
  return new Promise((resolve, reject) => {
    if(num % 2 == 0)
      resolve('OK');
    else
      foo.bar();
  });
}

makePromise(1).then((x) => {
  console.log(`then1 callback: ${x}`);
}).catch((r) => {
  console.log(`catch callback: ${r}`);
});
結果
catch callback: ReferenceError: foo is not defined

コールバック関数で例外が発生したときも、次のcatchのコールバック関数が呼ばれます。

function makePromise(num) {
  return new Promise((resolve, reject) => {
    if(num % 2 == 0)
      resolve('OK');
    else
      reject('NG');
  });
}

makePromise(2).then((x) => {
  console.log(`then1 callback: ${x}`);
  foo.bar();
}).catch((r) => {
  console.log(`catch callback: ${r}`);
});
結果
then1 callback: OK
catch callback: ReferenceError: foo is not defined

catchがない場合

executorでrejectを呼んだ場合や、executorやコールバック関数で例外が発生した場合に、catchがないと例外が発生します(nodeではUnhandledPromiseRejectionWarningが出ます)。

function makePromise(num) {
  return new Promise((resolve, reject) => {
    if(num % 2 == 0)
      resolve('OK');
    else
      reject('NG');
  });
}

makePromise(1).then((x) => {
  console.log(`then1 callback: ${x}`);
});
結果(chromeの場合)
Uncaught (in promise) NG

Promiseで発生した例外をtry - catchで捕まえることはできません(asyncとawaitを使えばできます)。次の例は意味なしです。

try {
  makePromise(1).then((x) => {
    console.log(`then1 callback: ${x}`);
  });
}
catch(e) {
  console.log(e);
}

ここまでの感想

Promiseのノリは、Promiseオブジェクトをじゃんじゃん作って、使わないものはほっておく、というものです。割とゴージャスなシステムと言えます。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?