Promise

(標準組み込みオブジェクト)
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise

Promise オブジェクトは、非同期処理の完了(もしくは失敗)の結果およびその結果の値を表す。
Promiseには、PromiseStatusというstatusがあり、3つのstatusがある。

  • pending:未解決 (処理が終わるのを待っている状態)
  • resolved:解決済み (処理が終わり、無事成功した状態)
  • rejected:拒否 (処理が失敗に終わってしまった状態)

new Promise()で作られたPromiseオブジェクトは、pendeingというPromiseStatusで作られる。
処理が成功した時に、PromiseStatusはresolvedに変わり、thenに書かれた処理が実行される。
処理が失敗した時は、PromiseStatusがrejectedに変わり、catchに書かれた処理が実行される。

Promiseインスタンスの作成

const promise = new Promise((resolve, reject) => {});

Promiseの引数には関数を渡し、その関数の第一引数にresolveを設定し、第二引数にrejectを任意で設定する(resolveもrejectも関数である)。
インスタンス作成直後のPromiseStatusは、pendeingである。

resolveさせる

// rejectは今回使わないため、引数から削除
const promise = new Promise((resolve) => {
  resolve();
}).then(() => {
  console.log("resolveしたよ");
});
>> resolveしたよ

resolve()を呼び出すことによって、PromiseStatusはresolvedに変わり、thenの処理が実行される。
resolve関数は引数を受け取ることができ、次に呼ばれるメソッド(then)の第一引数に渡してあげることができる。

const promise = new Promise((resolve) => {
  // 引数に文字列を渡す
  resolve("resolveしたよ");
}).then((val) => {
  // 第一引数にて、resolve関数で渡した文字列を受け取ることができる
  console.log(val);
});
>> resolveしたよ

rejectさせる

const promise = new Promise((resolve, reject) => {
  reject();
})
  .then(() => {
    console.log("resolveしたよ");
  })
  .catch(() => {
    console.log("rejectしたよ");
  });
>> rejectしたよ

上記の例ではthenの処理は実行されず、catchの処理が実行される。
しかし、PromiseStatusではrejectedにならない。
実は、catchにて実行される関数がreturnした値をresolveする。
簡単にいうと、catchはエラーを返したら満足して、解決済みの扱いとなる。
ゆえに、catchにて返されたpromiseオブジェクトのPromiseStatusはresolveになる。

Promiseのメソッドチェーン

処理が成功した時にthenに書かれた処理が実行され、処理が失敗した時はcatchに書かれた処理が実行されるが、それらの処理の後にまた別のthenを実行することができる。
さらに、returnで返した値を第一引数として次のthenに渡すことが可能である。

const promise = new Promise((resolve, reject) => {
  resolve("test");
})
  .then((val) => {
    console.log(`then1: ${val}`);
    return val;
  })
  .catch((val) => {
    console.log(`catch: ${val}`);
    return val;
  })
  .then((val) => {
    console.log(`then2: ${val}`);
  });
>> then1: test
>> then2: test

resolveされたため、1つ目のthenが実行されたあと、returnでvalを受け取り、2つ目のthenが実行された。

const promise = new Promise((resolve, reject) => {
  reject("test");
})
  .then((val) => {
    console.log(`then1: ${val}`);
    return val;
  })
  .catch((val) => {
    console.log(`catch: ${val}`);
    return val;
  })
  .then((val) => {
    console.log(`then2: ${val}`);
  });
>> catch: test
>> then2: test

rejectされたため、1つ目のthenは処理されず、1つ目のcatchが実行されたあとに2つ目のthenが実行された。

Promise.allとPromise.race

Promise.all

Promise.all()は配列でPromiseオブジェクトを渡し、全てのPromiseオブジェクトがresolvedになったら次の処理に進む。

const promise1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve();
  }, 1000);
}).then(() => {
  console.log("promise1おわったよ!");
});

const promise2 = new Promise((resolve) => {
  setTimeout(() => {
    resolve();
  }, 3000);
}).then(() => {
  console.log("promise2おわったよ!");
});

Promise.all([promise1, promise2]).then(() => {
  console.log("全部おわったよ!");
});
>> promise1おわったよ!
>> promise2おわったよ!
>> 全部おわったよ!

Promise.race

Promise.race()はPromise.all()と同じく配列でPromiseオブジェクトを渡し、どれか1つのPromiseオブジェクトがresolvedになったら次に進む。

const promise1 = new Promise((resolve) => {
 setTimeout(() => {
    resolve();
  }, 1000);
}).then(() => {
  console.log("promise1おわったよ!");
});

const promise2 = new Promise((resolve) => {
  setTimeout(() => {
    resolve();
  }, 3000);
}).then(() => {
  console.log("promise2おわったよ!");
});

Promise.race([promise1, promise2]).then(() => {
  console.log("どれか一つおわったよ!");
});
>> promise1おわったよ!
>> どれか一つおわったよ!
>> promise2おわったよ!

async/awaitを使う

Promiseの処理は、Promiseインスタンスを生成したり、resolve/rejectしたりするため、場合によっては冗長的になってしまう。
async/awaitを使うとPromiseの処理をより簡潔に書くことができる。

// async function を定義
const alwaysLateBoy = async (ms) => {
  await new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, ms);
  })
  // Promiseの結果が返ってくるまで実行されない
  console.log(`すまん!${ms}ms待たせたな。`)
};

async

asyncは非同期関数を定義する関数宣言であり、関数の頭につけることで、Promiseオブジェクトを返す関数にすることができる。そのような関数をasync functionという。

const asyncFunc = async () => {
  return 1;
};

async functionが値をreturnした場合、Promiseは戻り値をresolveし、その値はPromiseValueとして扱われる。

await

awaitは、Promiseオブジェクトが値を返すのを待つ演算子である。awaitは必ず、async function内で使う。

const asyncFunc = async () => {
  let x, y;
  x = new Promise(resolve => {
    setTimeout(() => {
      resolve(1);
    }, 1000 )
  })
  y = new Promise(resolve => {
    setTimeout(() => {
      resolve(1);
    }, 1000 )
  })
  console.log(x + y)
}
>> NaN

上記の例では、Promiseがresolveするのを待つ処理を書いてないため、console.log(x + y)ではNaNが返ってくる。
この処理を、Promiseがresolveするのを待つようにするには、このPromiseオブジェクトが全て実行完了するのを待つPromise.allを使えばできそうだが、awaitを使うともっと簡単に実装できる。

const asyncFunc = async () => {
  let x, y;
  // Promiseがresolveするまで待機
  x = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(1);
    }, 1000);
  });
  // Promiseがresolveするまで待機
  y = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(1);
    }, 1000);
  });
  console.log(x + y);
};
>> 2

console.log(x + y)は、上二つのPromiseが値を返すのを待ってから実行されるため、実行結果は2が返ってくる。

inserted by FC2 system