LoginSignup
61
64

More than 1 year has passed since last update.

【非同期処理】JavaScriptのPromise, async, awaitなどについての整理と使い方

Last updated at Posted at 2022-05-18

非同期処理/同期処理について

C言語やPythonから入った私はコードは上から順に実行されるものだと思っていました。しかし、JavaScriptはクライアントサイドの言語であり、 「ページ更新などレスポンスの即時性が求められているため非同期処理である」 とのことです。

~同期処理とは~

コードを上から順に処理していき、「サーバとのデータのやりとりなど時間がかかる作業を待って次の処理を行う」といったイメージです。

~非同期処理とは~

原則的にコードは上から順に処理していきますが、「サーバとのデータのやりとりなど時間がかかる作業と並行して次の処理を行う」

※詳細なイメージは@don-bu-rakkoさんの以下をご参考にして頂けると良いかと思います。
https://qiita.com/don-bu-rakko/items/b283829c4572a6425a5c
記事はこちらに移転したそうです

Promiseとasync/awaitを平たく言えば

Promiseは(オブジェクトとして)処理の状態を持たせて、処理の状態を変化させることで、引数で取る関数の処理を 非同期に進めたり待たせたりすることができます。
asyncで宣言した関数はawait(演算子)を使うことができます。awaitを挟むと、Promiseに変換することができ、処理終了後resolvedの状態であるPromiseとなります。resolvedになるまで関数内の他の処理が一時停止するので、 待つことができます。

Promiseについて

上記のような非同期処理であるJavaScriptにおいて、Promise(正確にはPromiseオブジェクトオブジェクトなんですね!)は処理の 「状態」 を表すものです。処理順序の決まり(=Promise)を指定するものって感じですね。
「状態」 は以下の3種類あります。(太字の英語は今後も出てくるかもしれないです)

Promise の状態

  • 待機 (pending): 初期状態。成功も失敗もしていません。
  • 成功 (resolved): 処理が成功して完了したことを意味します。
  • 失敗 (rejected): 処理が失敗したことを意味します。
    └細かくは、 fulfilledsettledもありますが、一旦上記3点でことは足ります。

Promiseの使い方

基本的なPromiseの記述

Promiseは引数に即時関数をとります。中の関数の引数にはresoleve関数reject関数をとります。

// 基本的な記述
const promise = new Promise(function(resolve,reject){
  //ここに処理を書きます
});
// アロー関数での記述
const promise = new Promise((resolve, reject) => {
  //ここに処理を書きます
});

まず、この時点での 「状態(=PromiseStatus)」pending です。(初期状態)
このPromiseStatusを変化させるために使うものがresolve()reject()です。
前者を呼び出すとPromiseStatusresolved となり、後者を呼び出すと rejected になります。
そしてPromiseStatusが変化すると、resolvedだと.thenが呼び出され、rejectedだと.catchが呼び出されます。
resolve()reject()は引数を取ると、それぞれ.then.catchの引数に使われます。

const promise = new Promise((resolve, reject) => {
  resolve(variable1);
  // reject(variable2);
})
  .then((variable1) => {
    console.log("resolveしました"); // こっちが実行される
  })
  .catch((variable2) => {
    console.log("rejectしました");
  });
const promise = new Promise((resolve, reject) => {
  // resolve(variable1);
  reject(variable2);
})
  .then((variable1) => {
    console.log("resolveしました");
  })
  .catch((variable2) => {
    console.log("rejectしました"); // こっちが実行される(エラー実行のイメージです)
  });

.then.catchの連鎖

resolveしたか、rejectされたかで.then.catchを連鎖させていくことができます。
変数の受け渡しはreturnを使用することで利用可能です。

なお、.catchが完了するとPromiseStatusresolvedになります。
(エラー後の処理を実行したことで、「エラーを解決した!」という理解)
そのため、.catchの後に.thenを記述することで.catch後も連鎖させていくことができます。

doSomething()
.then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

※コードは以下より引用
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises

Promiseの同時並行処理

複数のPromiseを実行する際に、以下のような処理を行いたいときに使用するメソッドがあります。

Promise のメソッド

  • 全てのPromiseが終了したら実行 Promise.all
  • いずれかのPromiseが終了したら実行 Promise.race
Promise.all([promise1, promise2]).then(() => {
  console.log("promise1とpromise2が終わりました");
});
Promise.race([promise1, promise2]).then(() => {
  console.log("promise1かpromise2が終わりました");
});

async/awaitとその使い方

asyncは非同期関数を定義する関数宣言で返り値はPromiseオブジェクトとなります。
asyncで宣言した関数を実行した際に返ってくるPromiseresolvedとなっているため、.thenで続けることができます。
asyncで宣言した関数内でawait演算子を使用するとその処理が終了するまで関数内の実行を一時停止することができます。
awaitの配置次第で前述のPromise.allPromise.raceを実現することもできます。

const asyncFunc = async () => {
  let x, y;
  // Promiseがresolveするまで待機
  x = await new Promise((resolve) => { // 2行下で返された1がawait演算子によってresolvedのPromiseとしてxに代入される
    setTimeout(() => {
      resolve(1); // 1000ms後にPromiseをresolvedにし、resolveの引数内の1を返す
    }, 1000);
  });
  // Promiseがresolveするまで待機
  y = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(2);
    }, 1000);
  });

  console.log(x + y);
};

なお、awaitに続く式がPromiseオブジェクトでない場合、resolvedとなったPromiseに変換されます。

async function f1() {
  var y = await 20;
  console.log(y); // 20
}

f1();

※コードは以下の記事より引用しました。
https://qiita.com/cheez921/items/41b744e4e002b966391a#await%E3%81%A8%E3%81%AF
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/await

Promiseオブジェクトになってしまったときの対応法

ところで、awaitを使うと、上記のようにPromiseで返ってくることがあります。Promiseオブジェクトの中身を取ってくる方法が以下です。
ここではPromiseになってしまったtmpの値を取ってみます。

// Promiseオブジェクトになってしまったtmp
tmp.then( function(value) {
  console.log(value) //valueがtmpの中身の値です
})

参考文献

61
64
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
61
64