LoginSignup
0
0

More than 3 years have passed since last update.

Promiseを理解する(非同期処理, コールバックも)

Posted at

非同期処理とは

ある処理が実行されてから終わるまで待たずに、次に控えている別の処理を行うこと。
そうすることで、時間のかかる処理を並行で処理し、その処理の完了を待たずに次の処理ができる。

JavaScriptはシングルスレッドなため、通常では並列で複数の処理ができない。そのため効率的に処理できるように考えられた仕組み。

コールバック関数とは

別の関数(高階関数)に渡すための関数。

// 例: setTimeout(callback, ms)
setTimeout(function() {
  console.log(5);
}, 1000);

setTimeout(() => { // アロー関数ver.
  console.log(5);
}, 1000);
NG
// 1秒毎に数値を表示してカウントダウンしたいが、このコードでは1秒後にすべて実行される
setTimeout(() => console.log(5), 1000);
setTimeout(() => console.log(4), 1000);
setTimeout(() => console.log(3), 1000);
setTimeout(() => console.log(2), 1000);
setTimeout(() => console.log(1), 1000);
setTimeout(() => console.log(0), 1000);
コールバック地獄
setTimeout(() => {
    console.log(5);
    setTimeout(() => {
        console.log(4);
        setTimeout(() => {
            console.log(3);
            setTimeout(() => {
                console.log(2);
                setTimeout(() => {
                    console.log(1);
                    setTimeout(() => {
                        console.log(0);
                    }, 1000);
                }, 1000);
            }, 1000);
        }, 1000);
    }, 1000);
}, 1000);

Promiseとは

JavaScriptにおいて、非同期処理の操作が完了したときに結果を返すもの。
(後で返す という「約束」)
コールバック地獄を避けるために考えられた仕組み。

ステータス

Promiseはステータス(状態)を持つ。

  • pending (保留中)
  • fulfilled (達成された)
  • rejected (却下された)

resolve, reject

Promiseの引数に設定するコールバック関数。

  • 処理が問題なく完了すればresolveが実行される。 (ステータスはfulfilled)
  • 問題があればrejectが実行される。 (ステータスはrejected)

then(), catch()メソッド

次に処理したいことを登録しておく。

  • then() : resolve()された場合の値を取得
  • catch() : reject()された場合の値を取得
getImage(file) // 画像ファイルの読み込みをする
  .then(image => compressImage(image)) // 画像を読み込んだら、圧縮する
  .then(cImage => saveImage(cImage)) // 圧縮が完了したら、その画像をDBに保存する
  .then(result => console.log(result)) // 保存した結果を出力する
  .catch(err => {throw new Error(err)})

このプログラムのどこでエラーが発生したとしても、エラーをキャッチする。
then()はfulfilled以外の場合は全てスルーして次の処理に渡すため。
エラーの場合、ステータスはrejectedになっているので、全てのthen()はこれをスルーする。最後に残ったcatch()だけがこれを掴むことができる。

Async と Await

コールスタックとキュー

同期処理、非同期処理ともに、まずはコールスタックに入る。
ただし、非同期処理のコールバックは、キューに入る。
コールスタックの処理が完了した後、キューに処理が残っている場合はそれをコールスタックに移し、処理が行われる。
そのため、非同期処理は同期処理より遅く処理される。= コードの上から順に処理されない。

Async

asyncが記述された関数は同期関数から非同期関数に変化し、常にPromiseを返す。

コード中にPromiseを返さないreturnがある場合、JavaScriptは自動的にその値を解決(resolve)されたPromiseにラップする。
つまり、次の全てのコードは同じ振る舞いをする。

1
async function func() {
    return 1;
}
2
function func() {
    return Promise.resolve(1);
}
3
function func() {
    return new Promise((resolve, reject) => {
        resolve(1);
    });
}
実行
func().then((num) => {
    console.log(num); // 1~3ともに、結果は1
});

Await

asyncの中では、awaitを指定した行はPromiseが解決されるのを待つ。= 上から順に処理される。(asyncは必須)
つまり、非同期処理を同期処理っぽく動かせる。

Promise.all()

並行で(非同期で)処理するほうが効率がよい場面では、awaitで待たせてはいけない。
Promise.all()を使うことで、複数の非同期処理を並列で処理し、全て完了させた後にコールバックできる。(Promise.all()を待たせる(awaitする))

bad
async function showNewData() {
  const allData = await fetchAllData();
  const oldData = await fetchOldData(); // allDataに待たされる
  showData(allData, oldData);
}
good
async function showNewData() {
  const [allData, oldData] = await Promise.all([fetchData(), fetchOldData()]);
  showData(allData, oldData); // allData, oldDataともに完了すると実行される
}
0
0
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
0
0