
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('呼び出し完了');