JavaScript の関数は「同期」処理される。処理が終わらないと次の処理に行けない。
ex. alert
関数でダイアログ出すとその場所でイベントから処理から全部止まる。
立脚すると、いつくるか分からない操作系のイベントとかタイマーとか、いつ終わるか分からない通信系の関数を、この原則で普通にブラウザーに定義すると、イベントくるまで全部待ちになってブラウザーまったく動かんってなってしまうので、この手の関数は「コールバック関数」を仕掛ける仕組みで実装された。
コールバック関数の設定だけなのでプログラムはさらっと「非同期」に下に抜けて、ブラウザーはイベントの発生を監視だけしとく。
// ex. addEventListener
target.addEventListener('onClick', function(e) {
// コールバック関数
});
かくして、ブラウザーはイベントが発生すると、処理中の関数と関数の呼び出しの「間」にコールバック関数を挟んで呼び出してくれる。のでいい感じに処理が進む。なんとなく見た目は同時に動いているように見えますが、全部関数と関数の隙間にコールバック関数が挟まって呼ばれています(並行処理)
さて、このようなコールバック関数の定義は該当関数の引数や戻りのオブジェクトのメソッドで行うですが、ブラウザー根っこの標準関数はともかく、それを使うライブラリー群は、ライブラリーによりけり、コールバック引数指定やらメソッド名やら書き方がまちまちで悲しい事態になってしまった。
// jQuery ajax (done と fail らしい)
$.ajax('/example', { 略 })
.done(function(data) {
// 次の処理
})
.fail(function(data) {
console.log(data);
});
Promise
の登場いろんな書き方が乱立してきたので、みんなが使えるキレイなインターフェースを誰かが決めればいいんじゃないってことで、JavaScript に Promise
オブジェクトが仕様として装備された。
これを知った多くのライブラリー群たちは Promise
に対応して(要は関数の返値が全部 Promise
になった)、コールバック系の処理がどれでも同じ Promise
が持つ .then()
.catch()
という決まったインターフェースでプログラムがかけるようになった。嬉しい。
// axios の例
axios.get('/example')
.then(function (response) {
// 次の処理
})
.catch(function (error) {
console.log(error);
})
// fetch の例
fetch('/example')
.then(function(response) {
// 次の処理
})
.catch(function(error) {
console.log(error);
});
// jQuery 3 の例
$.ajax('/example')
.then(function(response) {
// 次の処理
})
.catch(function(error) {
console.log(error);
})
でも、これでもまだなんとなくダサい?。コールバックとして書くのが面倒かも?。
async/await
の登場ということで、JavaScript に async/await
構文が追加され Promise のインターフェースが見た目として消すことができるようになりました。
await
Promise
対応ライブラリーの呼び出し側としては try {} catch{}
と await
でプログラムが下に下にまっすぐかけるようになりました。
// axios の例
try {
const response1 = await axios.get('/example1');
// 次の処理
const response2 = await axios.get('/example2');
// 次の処理
} catch(error) {
console.log(error);
}
// fetch の例
try {
const response1 = await fetch('/example1');
// 次の処理
const response2 = await fetch('/example2');
// 次の処理
} catch(error) {
console.log(error);
}
// jQuery 3 の例
try {
const response1 = await $.ajax('/example1');
// 次の処理
const response2 = await $.ajax('/example2');
// 次の処理
} catch(error) {
console.log(error);
}
見た目 await
のところでじっと待ってるように見えますが、内部的には Promise
のコールバックに書き換えられているのでこの関数内での待ちで他の処理が止まったりしません。すっきり、まっすぐ、かっこいい。
try {} catch {}
も、うざければ次のようにもかけます。
async
async
は await
される関数側が return new Promise(...)
resolve()
reject()
と書くのが面倒なので代わりに、
// async なし版
function example() {
return new Promise((resolve, reject) => {
try {
// ここで非同期関数を呼ぶ
// resolve は .then() を呼ぶ
resolve('success');
} catch(e) {
// reject は .catch() を呼ぶ
reject(err);
}
});
}
// async 版
async function example() {
try {
// ここで非同期関数を呼ぶ
// await に値が return される。
return 'success';
} catch(e) {
// await に例外が throw される。
throw e
}
}
と async
キーワードがいい感じに new Promise()
形式に書き換えてくれる感じになります。この関数からの戻値は見た目の return
と異なり Promise
型です。
async/await
は Promise
のシンタックスシュガーみたいなもんですね。new Promise
や .then()
catch()
を使わずに async/await
でキレイに Promise
を記述できます。