見出し画像

JavaScriptの非同期処理

JavaScriptの非同期処理の手法をまとめました。

1. コールバック

「コールバック」を使って、1秒かけて1加算する「非同期処理」の完了後に、後処理を行うコードを実装します。

// 非同期処理
function asyncProcess(value, callback) {
    setTimeout(() => {callback(value+1)}, 1000);
}

// 後処理
function postProcess(value) {
    console.log('結果: '+value)
}

// コールバックの利用
asyncProcess(0, postProcess);
console.log('呼び出し完了');
呼び出し完了
結果: 1

「コールバック」は、非同期処理を連続して実行する場合、階層構造が複雑になるという欠点があります。

asyncProcess(0, (data)=>{
    asyncProcess(data, (data)=>{
        asyncProcess(data, postProcess);
    });
});
呼び出し完了
結果: 3

この問題を解決するための命令が「Promise」になります。

2. Promise

「Promise」を利用することで、簡潔に非同期処理を実現することができます。

Promise()の引数には非同期処理を実行する関数を指定します。この関数の第1引数として渡される「resolve」は成功時のコールバックで、これを介して結果を返します。そして、then()の第1引数にエラー時のコールバックの通知先の関数を指定します。

// 非同期処理
function asyncProcess(value) {
    return new Promise((resolve) => {
        setTimeout(() => { resolve(value+1); }, 1000);
    })
}

// 後処理
function postProcess(data) {
    console.log('結果: '+data)
}

// Promiseの利用
asyncProcess(0).then((data) => {
    postProcess(data);
});
console.log('呼び出し完了');​
呼び出し完了
結果: 1

「Promise」は、非同期処理を連続して実行しても、階層構造が複雑になりにくいです。

asyncProcess(0)
   .then((data) => asyncProcess(data))
   .then((data) => asyncProcess(data))
   .then((data) => postProcess(data))
呼び出し完了
結果: 3

3. Promise の並列処理

上の例では非同期処理を同期的に順番に実行してきました。

asyncProcess() → asyncProcess() → asyncProcess() → postProcess()

Promise.all()を使うことで、非同期処理を複数同時に並列処理することができます。

asyncProcess() - +
asyncProcess() - + → postProcess()
asyncProcess() - +

全ての処理の完了時にコールバックが呼ばれ、処理数分の結果が渡されます。

// 非同期処理
function asyncProcess(value) {
    return new Promise((resolve) => {
        setTimeout(() => { resolve(value+1); }, 1000);
    })
}

// 後処理
function postProcess(data) {
    console.log('結果: '+data)
}

// Promiseの並列処理
Promise.all([
    asyncProcess(0),
    asyncProcess(1),
    asyncProcess(2)
])
.then((data) => postProcess(data))
console.log('呼び出し完了');
呼び出し完了
結果: 1,2,3

4. Promise のエラー処理

Promise()の第2引数として渡される「reject」はエラー時のコールバックで、これを介してエラーを返します。そして、then()の第2引数にエラー時のコールバックの通知先の関数を指定します。

// 非同期処理
function asyncProcess(value) {
    return new Promise((resolve, reject) => {
        setTimeout(() => { reject('エラーです') }, 1000);
    })
}

// 後処理
function postProcess(data) {
    console.log('結果: '+data)
}

// Promiseの利用
asyncProcess(0).then((data) => {
    postProcess(data);
}, (error) => console.log(error));
console.log('呼び出し完了');

5. async / await

「async / await」を使うことで、「Promise」による非同期処理をさらに簡潔に効率よく記述できます。

◎ async
関数の頭に「async」と記述することで、その関数が「Promise」を返すようになります。

async () => { 処理 }

◎ await
関数の頭に「await」と記述することで、「Promise」の処理結果が返ってくるまで次の行に進まなくなります。「await」が使えるのは、「async」定義された関数内のみになります。

「async / await」で書き直したコードは、次のとおりです。

// 非同期処理
function asyncProcess(value) {
    return new Promise((resolve) => {
        setTimeout(()=>{ resolve(value+1) }, 1000);
    })
}

// 後処理
function postProcess(data) {
    console.log('結果: '+data)
}

// async / awaitの利用
(async () => {
    let data = 0;
    data = await asyncProcess(data);
    data = await asyncProcess(data);
    data = await asyncProcess(data);
    postProcess(data);
})();
console.log('呼び出し完了');
呼び出し完了
結果: 3

6. async / await の並列処理

「async / await」の並列処理は、次のように記述できます。

// 非同期処理
function asyncProcess(value) {
    return new Promise((resolve) => {
        setTimeout(() => { resolve(value+1); }, 1000);
    })
}

// 後処理
function postProcess(data) {
    console.log('結果: '+data)
}

// async / wait の並列処理
(async () => {
    const result0 = asyncProcess(0);
    const result1 = asyncProcess(1);
    const result2 = asyncProcess(2);
    postProcess([await result0, await result1, await result2]);
})();
console.log('呼び出し完了');
呼び出し完了
結果: 1,2,3

7. async / await のエラー処理

catch()の第1引数にエラー時のコールバックの通知先の関数を指定します。

// 非同期処理
function asyncProcess(value) {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{ reject('エラーです') }, 1000);
    })
}

// 後処理
function postProcess(data) {
    console.log('結果: '+data)
}

// async / awaitの利用
(async () => {
    let data = 0;
    data = await asyncProcess(data);
    data = await asyncProcess(data);
    data = await asyncProcess(data);
    postProcess(data);
})().catch((error) => console.error(error));
console.log('呼び出し完了');


いいなと思ったら応援しよう!