async/await 風にジェネレータを使う
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ ES7 の async await 機能が便利そう
◆ ジェネレータで近いことをやってみます
◆ ジェネレータで近いことをやってみます
前にジェネレータで非同期関数を楽にできないかなーとこんなのを作ってましたが 結局いままでと大差ない非同期処理を配列に並べて順に実行していくだけでした
ですが 最近 async await という仕組みを知って 「おー便利! しかも yield で近いことできそう 」 と思ったのでやってみることにします
JavaScript が最初に導入する仕組みってものでもなく Python だったり .NET などでも使われている言葉です
構文では c# のに近そう?
ぐぐってみても あれこれライブラリ使ってみましたとかでシンプルにどういう書き方でどう動くかを綺麗にまとめてるのが見当たらなかったのですが 簡単にまとめるとこんなものなようです
(公式 spec も何かと複雑で見づらくてパスしました)
asyncFunction は非同期な関数です
普通は 非同期関数の返り値では目的のものが入っていないです
Promise を返して返り値を通してやることを then でセットだけしておいて 結果が来たらやるっていうのが最近の方法かと思いますが await を使えば そこでちゃんとストップして続きからしてくれるそうです
await を使う関数には目印に async をつけておきます
val にはいきなりほしい値が入っていて Promise を通す必要もないのです
すっごく便利そうです!!!
ここまで簡単にはできないですが 近いことをやってみようと思います
非同期な関数には URL を指定してダウンロードするものとスリープを用意します
メイン部分はこんなのです
実行すると……
おぉ。。
ちゃんとできてますね
スリープのところも差がだいたい 1000 ですし 書いた通りの並びで実行されています
await の代わりに yield afunc( ) を書きます
afunc は関数で引数には 非同期な関数とその関数を呼び出す時の引数を渡します
async 関数を実行するときは acall 関数に async 関数とその関数に渡す引数を渡します
これだけでおっけい
かなり楽に使えるのではと思います
今回はあまり複雑にしたくなかったので中では Promise じゃなくて 昔ながらのコールバック関数を使っています
なので wait などの非同期関数を作るときには最後にコールバック関数を受け取るようにしておかないとダメで それと afunc の呼び出しでは引数の省略ができなかったりでちょっと不便です
まぁこれは Promise にしたら扱いやすくなるのかと思います
あとは try catch
やるなら acall 関数が Promise 作ってそこで受け取るくらいしかできないのかな
async 関数内で await 部分を含んで try catch を書かれてもどうしようもなさそう
JavaScript で再現してますが たしかこんな感じです
await なしで非同期関数を実行して その値が必要になるところまでは進めます
「ここは直ぐに表示」というのはすぐに表示されます
そして await があるところまで行くと await で指定された変数に値が入るまで待つようです
ちょっと複雑ですが この機能もありかなと思います
JavaScript のを簡単に見た感じではこういう例は見当たらなかったのですが どうなるんでしょう
ですが 最近 async await という仕組みを知って 「おー便利! しかも yield で近いことできそう 」 と思ったのでやってみることにします
async/await
ES7 からのものらしいですJavaScript が最初に導入する仕組みってものでもなく Python だったり .NET などでも使われている言葉です
構文では c# のに近そう?
ぐぐってみても あれこれライブラリ使ってみましたとかでシンプルにどういう書き方でどう動くかを綺麗にまとめてるのが見当たらなかったのですが 簡単にまとめるとこんなものなようです
(公式 spec も何かと複雑で見づらくてパスしました)
async function sample(){
var val = await asyncFunction(1)
console.log(val)
}
sample()
var val = await asyncFunction(1)
console.log(val)
}
sample()
asyncFunction は非同期な関数です
普通は 非同期関数の返り値では目的のものが入っていないです
Promise を返して返り値を通してやることを then でセットだけしておいて 結果が来たらやるっていうのが最近の方法かと思いますが await を使えば そこでちゃんとストップして続きからしてくれるそうです
await を使う関数には目印に async をつけておきます
val にはいきなりほしい値が入っていて Promise を通す必要もないのです
すっごく便利そうです!!!
ジェネレータで頑張る
でも await で待機してデータが来たら続きをやるっていうのは 一度中断してあとから続きをやるジェネレータの yield に似てますよねここまで簡単にはできないですが 近いことをやってみようと思います
function acall(gen, ...args){
var ite = gen(...args)
!function recur(pre_value){
var ret = ite.next(pre_value)
ret.done || ret.value(recur)
}()
}
function afunc(fn, ...args){
return cb => fn(...args, cb)
}
こんな関数を準備しますvar ite = gen(...args)
!function recur(pre_value){
var ret = ite.next(pre_value)
ret.done || ret.value(recur)
}()
}
function afunc(fn, ...args){
return cb => fn(...args, cb)
}
非同期な関数には URL を指定してダウンロードするものとスリープを用意します
function httpGet(url, cb){
fetch(url).then(e => e.text()).then(e => cb(e))
}
function wait(msec, cb){
setTimeout(cb, msec)
}
fetch(url).then(e => e.text()).then(e => cb(e))
}
function wait(msec, cb){
setTimeout(cb, msec)
}
メイン部分はこんなのです
function* sample_gen(message){
console.log("wait", Date.now())
yield afunc(wait, 1000)
console.log("waitend", Date.now())
var text = yield afunc(httpGet, "/")
console.log(message)
console.log(text)
}
acall(sample_gen, "この後にURLのデータ:")
console.log("wait", Date.now())
yield afunc(wait, 1000)
console.log("waitend", Date.now())
var text = yield afunc(httpGet, "/")
console.log(message)
console.log(text)
}
acall(sample_gen, "この後にURLのデータ:")
実行すると……
wait 1455345512307
waitend 1455345513308
この後にURLのデータ:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTM...............
waitend 1455345513308
この後にURLのデータ:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTM...............
おぉ。。
ちゃんとできてますね
スリープのところも差がだいたい 1000 ですし 書いた通りの並びで実行されています
使い方
async の代わりにジェネレータじるしの * をつけますawait の代わりに yield afunc( ) を書きます
afunc は関数で引数には 非同期な関数とその関数を呼び出す時の引数を渡します
async 関数を実行するときは acall 関数に async 関数とその関数に渡す引数を渡します
これだけでおっけい
かなり楽に使えるのではと思います
今回はあまり複雑にしたくなかったので中では Promise じゃなくて 昔ながらのコールバック関数を使っています
なので wait などの非同期関数を作るときには最後にコールバック関数を受け取るようにしておかないとダメで それと afunc の呼び出しでは引数の省略ができなかったりでちょっと不便です
まぁこれは Promise にしたら扱いやすくなるのかと思います
あとは try catch
やるなら acall 関数が Promise 作ってそこで受け取るくらいしかできないのかな
async 関数内で await 部分を含んで try catch を書かれてもどうしようもなさそう
これだけ?
ところで 別言語の async をみていると await の後の関数が非同期関数って言うものじゃなさそうな例もありましたasync function sample(){
var aval = asyncFunction(1)
console.log("ここは直ぐに表示")
var val = await aval
console.log(val)
}
sample()
var aval = asyncFunction(1)
console.log("ここは直ぐに表示")
var val = await aval
console.log(val)
}
sample()
JavaScript で再現してますが たしかこんな感じです
await なしで非同期関数を実行して その値が必要になるところまでは進めます
「ここは直ぐに表示」というのはすぐに表示されます
そして await があるところまで行くと await で指定された変数に値が入るまで待つようです
ちょっと複雑ですが この機能もありかなと思います
JavaScript のを簡単に見た感じではこういう例は見当たらなかったのですが どうなるんでしょう