Edited at

Async Functions はなぜ良いのか

More than 1 year has passed since last update.


忙しい人向け

読みやすいから。

JavaScriptは如何にしてAsync/Awaitを獲得したのかもぜひ読みましょう。


ひまな人向け

Chrome は55から、 Node.js は7.6 からデフォルトで Async Functions (async/await) をサポートしました。

これは明らかに世界平和に近づく出来事です。


サンプル問題

Async Functions は事前に回数が決まっている非同期処理も書きやすくなりますが、真価を発揮するのは何回実行するか実行時までわからない非同期処理です。

今回は例として、(最大の試行回数、試行間の待機時間(ms)、実際の処理の Promise)を引数として受け取り、 Promise を返すというプログラムを考えてみます。


  • 最大試行回数は10回

  • 試行間の待機時間は100 ms

  • 処理の内容は


    • ランダムで 0〜100 の数字を生成する

    • 95 以上なら成功(resolve)

    • それ未満なら失敗(reject)、やり直す



というものにします。つまり、結果が

次はうまくやるでしょう: 93

次はうまくやるでしょう: 46
次はうまくやるでしょう: 34
次はうまくやるでしょう: 37
できました: 96

だったり

次はうまくやるでしょう: 79

次はうまくやるでしょう: 91
次はうまくやるでしょう: 17
次はうまくやるでしょう: 92
次はうまくやるでしょう: 46
次はうまくやるでしょう: 27
次はうまくやるでしょう: 83
次はうまくやるでしょう: 5
次はうまくやるでしょう: 88
できませんでした: 14

だったりするやつです。


実装例: Promise


retryPromise.js

const retryWithPromise = (maxRetries, retryInterval, fn) => {

return new Promise((rootResolve, rootReject) => {
const f = (tries, maxRetries, retryInterval, fn, parentResolve, parentReject) => {
return new Promise((resolve, reject) => {
fn().then(r => parentResolve(r)).catch(e => {
if (tries >= maxRetries) return parentReject(e);

console.log(`次はうまくやるでしょう: ${e}`);

setTimeout(() => f(tries + 1, maxRetries, retryInterval, fn, parentResolve, parentReject), retryInterval);
});
});
};

f(1, maxRetries, retryInterval, fn, rootResolve, rootReject);
});
};

const test = () => new Promise((resolve, reject) => {
const result = Math.round(Math.random() * 100);

if (result >= 95) {
resolve(result);
} else {
reject(result);
}
});

retryWithPromise(10, 100, test)
.then(r => console.log(`できました: ${r}`))
.catch(e => console.log(`できませんでした: ${e}`));



実装例: Async Functions


retryAsync.js

const retryAsync = async (maxRetries, retryInterval, fn) => {

for (let tries = 1; tries <= maxRetries; tries++) {
try {
return await fn();
} catch (e) {
if (tries >= maxRetries) throw e;
console.log(`次はうまくやるでしょう: ${e}`);
await new Promise(r => setTimeout(() => r(), retryInterval));
}
}
};

const test = async () => {
const result = Math.round(Math.random() * 100);

if (result >= 95) {
return result;
} else {
throw result;
}
};

retryAsync(10, 100, test)
.then(r => console.log(`できました: ${r}`))
.catch(e => console.log(`できませんでした: ${e}`));



まとめ

一目瞭然ですね。もし前者の方が理解しやすい人がいたら手を挙げてください。

環境がネイティブで対応してなくても Babel や TypeScript を使って async/await の庇護と恩恵を受け、完璧に幸福になりましょう!