JavaScriptで基礎から学ぶ非同期処理


非同期処理とは?

同期処理と非同期処理

同期処理:決められた順番通りに実行される
非同期処理:実行順番は実行時によって異なる
文字で見るより、実際に見たほうが早いと思います。

ex.
同期処理

console.log("1");
console.log("2");
console.log("3");
console.log("4");
console.log("5");

このコードは順番通りに実行されます。

非同期処理

// それぞれランダム時間待ってからログ出力する
setTimeout(function(){console.log(1)}, Math.random()*1000);
setTimeout(function(){console.log(2)}, Math.random()*1000);
setTimeout(function(){console.log(3)}, Math.random()*1000);
setTimeout(function(){console.log(4)}, Math.random()*1000);
setTimeout(function(){console.log(5)}, Math.random()*1000);

ランダム時間待ってからログ出力しますが、必ず数字の順番のとおりに出力されるわけではありません。
実際に実行してみた結果が下記です。

ランダム秒待って1を出力→ランダム秒待って2を出力…と進まないのがわかるでしょうか?
ここでは、非同期だと処理の完結を待たないとだけ覚えてください。

非同期処理を使うべき場所

非同期処理のメリットは順番に縛られないことです。
主に重い処理、時間のかかる処理にて使用されます。
例えば、httpでアクセスをして結果を取得するなど。(アクセスして結果を取得するまでの間、他の処理を実行できる)

非同期処理の書き方

最初に単語を羅列しておきます。

  1. async:非同期
  2. promise:(処理を)約束する
  3. resolve:(処理を)解決する

まず最初に同期処理で書いてみます。

function resolveAfterHogehogeSeconds(x) {
    let i = 0;

    // 超絶重い処理
    do {
        i++;
        console.log("");
    } while (i < 10000);

    return x;
}

function waitGreet() {
    console.log(resolveAfterHogehogeSeconds("おはよう"));
    console.log(resolveAfterHogehogeSeconds("こんにちは"));
    console.log(resolveAfterHogehogeSeconds("おやすみ"));

}

waitGreet();

実行した様子は下記です。

実行に時間がかかっている様子が見て取れると思います。

非同期で書き直してみましょう。


function resolveAfterHogehogeSeconds(x) {
    return new Promise (resolve => {    
        let i = 0;

        // 超絶重い処理
            do {
                i++;
                console.log("");
            } while (i < 10000);

        resolve(x);
    });
}

function waitGreet() {
    resolveAfterHogehogeSeconds("おはよう").then(x => {
        console.log(x);
    });
    resolveAfterHogehogeSeconds("こんにちは").then(y => {
        console.log(y);
    });
    resolveAfterHogehogeSeconds("おやすみ").then(z => {
        console.log(z);
    });
}

waitGreet();

重い処理の部分が非同期的に実行されているのがわかると思います。
ちなみに.then(の次にあるコードはアロー関数(無名関数のシンタックスシュガー)です。
細かく言えばthisについてなどが違うのですが、ここにおいてはそう思っておいても大丈夫です。

なお、asyncを利用するともっと簡単に書くことができます。


async function resolveAfterHogehogeSeconds(x) {
    let i = 0;

    // 超絶重い処理
    do {
        i++;
        console.log("");
    } while (i < 10000);

    return x;
}

function waitGreet() {
    resolveAfterHogehogeSeconds("おはよう").then(x => {
        console.log(x);
    });
    resolveAfterHogehogeSeconds("こんにちは").then(y => {
        console.log(y);
    });
    resolveAfterHogehogeSeconds("おやすみ").then(z => {
        console.log(z);
    });
}

waitGreet();

非同期処理を連続して処理する

非同期処理を連続して処理する場合は下記のように書きます。(ほぼ同期処理)


async function resolveAfterHogehogeSeconds(x) {
    let i = 0;

    // 超絶重い処理
    do {
        i++;
        console.log("");
    } while (i < 10000);

    return x;
}

async function waitGreet() {
    console.log(await resolveAfterHogehogeSeconds("おはよう"));
    console.log(await resolveAfterHogehogeSeconds("こんにちは"));
    console.log(await resolveAfterHogehogeSeconds("おやすみ"));
}

waitGreet();

await:(処理を)待つ
要するに、resolveAfterHogehogeSecondsの実行完了を待ってから次の処理に進みます。
コードを比較すればわかりますが、thenの書き換えです。
なお、待つということからわかりますが、Promiseに対してしか使用できず、
かつasyncの関数の内部でしか使用できません。

参考