setTimeoutじゃできないやつをasync/awaitで実現する
setTimeoutで実現したいと思っても出来ないことがあります。
非同期はpromiseのthenで書いたことはあったのですが、苦手意識がありました
今回はasync/awaitで書き換えてみました
参考記事
setTimeoutだと意図した順番にならない時がある
例えば「初回」→3秒後「2回目」→1秒後「3回目」と出力したいとき、setTimeoutを使うと思った結果にならないことがあります
const message = text => console.log(text);
function messFunc() {
message("初回");
setTimeout(() => { message("2回目")}, 3000);
setTimeout(() => { message("3回目")}, 1000);
}
messFunc();
//=> 初回 3回目 2回目
上から順にsetTimeoutが実行され、単に指定時間経過後に処理が実行されるからです
setTimeoutで順番通りに実行するためには、以下のように前の処理の時間を考慮し、考えて書き換えなくてはなりません
const message = text => console.log(text);
function messFunc() {
message("初回");
setTimeout(() => { message("2回目")}, 3000);
setTimeout(() => { message("3回目")}, 4000);
}
messFunc();
//=> 初回 2回目 3回目
これが例えば「3回目を初回の2秒後に、2回目を3回目の4秒後」のように変更したいとなったときは、また処理の時間を考慮しながら書き換えなくてはなりません
処理が複数になればなるほど煩雑になり、正直面倒ですし可読性も良くないです
async/awaitを使う
これを解決するために、Promiseとasync/awaitで非同期関数を同期的に扱い、特定の処理が終わってから次の処理を実行する関数を記述します
const promiseFunc = (func, time) => {
return new Promise(resolve => {
setTimeout(() => {
func();
resolve();
}, time);
});
}
const a = () => console.log("a");
const b = () => console.log("b");
const c = () => console.log("c");
async function loading() {
promiseFunc(a, 0);
await promiseFunc(b, 3000);
await promiseFunc(c, 1000);
}
loading();
//=> a b c
任意の関数をn秒後に実行した後、resolveを返すpromiseFunc関数を設定します
await指定された関数は前の処理がresolveを返してから実行します
そのためawait指定をすることで、「a」→3秒後「b」→1秒後「c」のように想定どおりにコンソールに表示されます
何の処理の後にどのような処理を走らせるか、見通しが良くなります
resolveの何たるかは参考サイトがわかりやすいです
複数の処理を非同期で同時に実行する
以下のようにPromise.allを使うと、複数の処理を非同期で同時に実行できます
const promiseFunc = (func, time) => {
return new Promise(resolve => {
setTimeout(() => {
func();
resolve();
}, time);
});
}
const a = () => console.log("a");
const b = () => console.log("b");
const c = () => console.log("c");
const d = () => console.log("同時です-1");
const f = () => console.log("同時です-2");
async function loading() {
promiseFunc(a, 0);
await promiseFunc(b, 3000);
await promiseFunc(c, 1000);
await promiseFunc(() => {
Promise.all([
promiseFunc(d, 1000),
promiseFunc(f, 1000)
]);
}, 500);
}
loading();
//=> a b c 同時です-1 同時です-2
また引数の関数は無名関数でも構いません
await promiseFunc(() => console.log("無名関数です"), 3000);
非同期で複数処理が終わってから次の処理を行う例
2つの非同期関数を待ってから次の処理を行う場合の例です
const promiseFunc = (func, time) => {
return new Promise(resolve => {
setTimeout(() => {
func();
resolve();
}, time);
});
}
const a = () => console.log("a");
const b = () => console.log("b");
const c = () => console.log("c");
async function loading() {
Promise.all([
promiseFunc(a, 1000),
promiseFunc(b, 3000)
]);
await promiseFunc(c, 1000);
}
loading();
//=> a b c
2つのpromiseFuncが同時に実行され、そこから1秒後にaの処理、3秒後にbの処理が行われ、すべての処理が完了し1秒後にcが実行されます
最後に
async/awaitは僕も何故か苦手意識がありましたが、上手く使えれば色々便利です
例えばローディングアニメーションが終わってからファーストビューのスライダーを開始させるとか、スタイルの付与処理を終えたら次のスタイルを付与するなど
前の処理を待ちたい時に良さそう
最初は従来のPromise(then)に触れてからの方がいいかもしれません
Discussion