ES2017のasync/awaitのキソ練習

最近(2018-05現在)はライブラリで Promise 前提のものが増えてきてるしサンプルにもしれっと普通の顔で async/await が登場するので慣れておかないといけない。特に Node.js ではもう LTS である 8 (正確には 7 の途中)で標準的に使えるので、今後はもっと当たり前に登場してくる機会が増えてくることが予想される。1

ということで今回はasync/awaitの練習。ただし await の入れ子や Promise.all で束ねるといった複雑なことは扱っていない。

※ Promise.all() の話は async/awaitやPromiseで気をつけること - あーありがち(2018-06-15) で!

またここで扱っているのはあくまで ES2017 の async/await であって他の独自に実装された async ライブラリのことでもないです。確認は素の Node 8 で行ってます。

まずはPromise

雰囲気だけならこちらをどうぞ。 jQuery.deferredを経由しつつES2015に入ったPromiseの雰囲気を味わう - あーありがち(2016-06-19)

async/await の理解には Promise の理解が欠かせない。なぜなら async/await はあくまで Promise を同期的に、手続き的な処理のように書けるだけだから。

さて。

まず Promise がやってくれるのは非同期処理を抽象化し、統一インターフェイスを提供すること。

プロミスはいつ生成されるかわからない値のプロキシのようなものであり、成功したり失敗したりする動作を扱う時の理想的なパターンです

JavaScriptにおける非同期パターン #翻訳 « Tatyusa's Note

例えば Node.js 標準の callback 関数の受け取り方と jQuery のそれはルールが異なる。2これが ES6 Promise では then(), catch() に制約されるし、Promise は 定義された状態しか持たない。そのうちの一部が fulfilled や rejected である。

つまり、「Promise を返す」とさえ言えばあとは使い方は「Promise を勉強してくれ」で済む。実装者間の共通言語、インターフェイスとして非常に優れている。(ただし学習コストは一時的には上がる。)

Promiseの例

Promise は上にあるように非同期処理に特化したものではない。

単なる即値であっても Array であっても Promise オブジェクト足り得る。例えば以下のような超短いコードも Promise オブジェクトではある。

const p1 = Promise.resolve('a')

これを console.log すると

Promise { 'a' }

と表示される。よく分からないけど 'a' を返してくれる Promise なんだなという雰囲気は伝わってくる。

実際に欲しいのは Promise じゃなくて値だ? ごもっとも。そのためには arrow function を使えば以下のように非常に短い記述で取り出せる。3

p1.then(data => console.log(data))

async functionとawait演算子

まずは基本的なことから

  • async function は Promise を返す
    • Promise を return していない場合は自動的に補完される
    • ただし callback を Promise に変換するのは自分で書くべき

例えば上の Promise は async function を使うと以下のように書ける。

async function asyncValue() {
  return 'a'
}

正確には Promise を返す function を定義しただけなので、同じ挙動をするように手直しをすると、関数の即時実行を使って以下のようになる。

const p2 = (async function() { return 'a' })()

だるいですね。arrow function でこうしちゃう。

const p2 = (async () => 'a')()

callbackを受け取るものは明示的にPromiseに組み直す

async function は確かに自動で Promise を返すんだけど、setTimeout などの callback を受け付けるものは自分で wrap しないと resolve 時に何が渡ってくるのか分からないので、以下のように変換する。

async function lazyA() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('a')
    }, 1000)
  })
}

この場合、async はあってもなくても Promise を返す。Promise が二重化することはなくて、Promise を返すものはそのままスルーしてくれる。ということで、

Promise を返すものは慣習として async を頭に付けちゃう

という運用でよいような気がする。(だったらなくてもいんじゃね?と思うかもしれないが、以下の理由により、逆の運用の方がよいのだ。)

awaitは「解決済み」の値を取り出す演算子

解決済みまで待つので例えば上の lazyA() を例にとると

await lazyA()

とすると then() とか面倒なことしなくても 'a' を取り出すことができる。

ただしawaitは自由には使えない

  • async の中でしか使えない

逆に言うと async function の中では他の async function を await で呼べるということである。

  • 解決済みの Promise の値をオブジェクトとしてメソッドチェインはできない
(async () => {
  const a = await lazyA()
})()

は正しく 'a' になる。しかし以下のようには書けない。

(async () => {
  (await lazyA()).length
})()

つまり以下のようにも書けない。

(async () => {
  (await lazyArray()).map(e => ..)
})()

こういう書き方にしようとすると結局 Promise が顔を出してくる。

await

  • JavaScriptMDN</a>

ここで

[rv] = await expression;

と書かれているのはどうも「値を受け取る人」が必要っぽい。

awaitの後ろは切る

ということで同期処理を始める前に await の結果は代入して処理は切れるようにしておく。以下のような感じ。

(async () => {
  const arr = await lazyArray()
  arr.map(e => e * 2)
})

ただし後ろに繋げるのはダメだが、前ならよい。こんな感じ。(はっきり代入ではないが、扱いとしては同じっぽい。)

let newVal = []

for ( let e on lazyArray ) {
  newVal.push(e * 2)
}

参考

  1. Node 8 が出たのはちょうど1年前の2017年5月です! 

  2. jQuery では使いたい API によっても異なる。 

  3. ただし取り出しが then() の中の callback という形になってしまう。Promise は callback 地獄を解決してくれるのでは?という気持ちになる部分でもある。 

WindowsでVagrantパッケージを入れたときにできることとVagrantfileをrakeで生成するアイディア

Vagrantが環境変数をセットする

インストーラで Vagrant を入れると Vagrant パッケージのディレクトリの中の

bin/

に PATH が通る。

そして vagrant コマンドを起動している間だけ

embedded/bin/

に PATH が通る。

そこに ruby, gem, rake があるので実は Vagrant を入れてあれば Rake タスクを実行できる。

これで Vagrantfile を直接置くのではなく Vagrantfile.erb から生成するといったことができる。

「よく似ているけど微妙に違う開発環境」を用意するために Vagrant を使おうとしている場合、これは便利に使えるかも。

例えば

Railsアプリの環境1192.168.0.xx
Railsアプリの環境2192.168.0.yy
Railsアプリの環境3192.168.0.zz

みたいなテーブルを Rakefile の中に持っておいて、微妙に違う設定を Vagrantfile に反映する、といったことができる。これで provisioning 用の Chef の cookbook はほとんど共通だけど、ちょっとだけパラメータを変えて実行したいという要求に応えやすい気がする。

vagrant_cmd plugin

nazoking/vagrant_cmd

Windows 環境で

vagrant plugin install vagrant_cmd
vagrant cmd

とすると上の環境変数をセットした状態の cmd ができあがる。

これで Vagrantfile 生成用に Rake を叩くことができる。

うむうむ。

少し注意が必要なのは、この追加される PATH の方が優先されるので、例えば

RubyInstaller for Windows

が入れてあってもこの環境では Vagrant で入れた Ruby が優先されるということ。分かっていればそうでもないが、慣れないとややこしい。

例えばこの plugin を入れていなくても vagrant コマンドの実行途中に ^C で break してしまうと環境変数を書き戻す処理が実行されずに PATH が書き変わったままになってしまう。これは分かっていないと混乱する。

AndroidのメモアプリをFlickNoteからNotationalAccelarationに変えた

以前書いたように Evernote + Simplenote の2大メモ体制 で臨んでいる1んだけど、ある日突然 Simplenote 用の Android アプリである FlickNote が同期に失敗するようになった。

しばらく Android の方は仕方なく Evernote でメモして PC で素早く書きたいときは Notational Velocity から Simplenote へと、なんだかおかしな使い方をしていたんだけど、さすがに不便なので探し直した。

結果、Notational Accelaration にした。

同期の速度も特別速くないし、自分のきらいな黒地に白文字の画面なんだけど、

同期の失敗が比較的少なく、安定して動く

のでこれにした。有料アプリも試してるんだけど、Android 用の Simplenote クライアントの開発が停滞しているのか、どれもそれほど優秀な感じがしない。FlickNote がやっぱりいちばんいいと思うんだけど、いかんせん

アカウント新規作成後の最初の同期以外は失敗する

という豪快な状態で、さすがにこれはちょっと使いものにならないので諦めることにした。

なお次点は AndroNoter だったが、同期するとノートがダブってしまうという問題があったので見送った。

  1. 何に? 

gemを作るとき、プロジェクトを作るときには結局何を使うと嬉しいのだろう

これまで gem は使うだけで作ったことがなかった。github や coderepos に上げてある自作のツールも gem 使えたらもう少し楽かなぁなんてことを思うんだけど、何を使って作るのがいいのかもよく分からないのでちょっと調べてみた。

というか何年も溜め込んでいただけのブックマークを広げてみた。

名前だけは newgem や Hoe を知っていたんだけど、なんかこういう似たようなものがいっぱいある状況はあまり好きではなく、決定版はないのかなーと思っていたところで以下のような記事を見つけた。

cutagem - Yet Another newgem っぽいなにか - 冬通りに消え行く制服ガールは、夢物語にリアルを求めない。 - subtech

こういう情報は実に嬉しい。howto は howto でありがたいんだけど、それぞれのツールの howto だけあっても、で、結局何?みたいな気持ちになるのも事実1。ちゃんと比較というか好みを挙げてくれるものはあまり見る機会がなく、とても参考になる。

これだけじゃなんなので自分でも試してみた。

  • hoe
    • そんなにたいしたことはしない。こういう風に書きなよっていうひな形のファイルがダダっとできるだけで、確かに Manifest のメンテとかそれなりに面倒くさそう。
  • newgem
    • 依存が多い。activesupport とか hoe とか。その割にできあがる Rakefile が決め打ちでいやな感じはする。というか基本は Rails 向けかなぁ? 個人的にはあんまり用ないような?
  • cutagem
    • 決定版ぽいんだけど最近いじってないのはなんでだろう…。基本的に rubyforge しか考えてないっぽいのは 0.0.7 の話?2 できた Rakefile はいじりやすくて、とてもいい感じ。
  • gemify
    • gem の作成に向いているのであってプロジェクトのひな形は作ってくれない。

ここに来て新事実発見。

プロジェクトのひな形を作るツールと gem を作るツールは分離しててもよい。

そうか。hoe + gemify くらいがお手軽というかスタンダードに近いと考えていいのかなぁ?

ruby-toolbox 的には jeweler らしいんだけど自分の環境は .gitconfig が link だからなのか jeweler が使えなかった。じゃあ newgem を飛ばして知名度的には bones かしら?

……。なんか余計に選択肢が増えてしまった。どうしよう。


このあとボクはどっち方面へ向かっていったらいいんでしょうか? cutagem が好みに合うならそっちまっしぐらでいんじゃね?とか、bones も悪くないよなどの情報があるととても喜びます。

  1. ごめんなさいごめんなさい 

  2. まだ 0.0.8 は試してない 

Tiger の Mail.app うぜぇ

自分で使う気はないんだけどデフォルトの設定が変じゃねーか?と思って調べるために立ち上げてみた。ウィザード形式でアカウントの設定ができないと通常のウィンドウを拝むことすらできねぇ。それ自体はすげーいやだけど Outlook Express でもお馴染みの方法。問題は途中途中でいちいち設定したサーバに接続にいって丁寧に確認しくさりやがってくださること。

遅いんだよ!

なんだこれ。なんでこう前より悪くするソフトが次々出てくるんだ。

今日はですますで始まったのに一気に「うぜぇ」まで気分が変わってしまいましたとさ。あーやだやだ。

Yahoo だからなのか Yahoo さえもなのか

Yahoo.com のトップページのレイアウトが変わったんですね。しかも横幅が固定で広がって。なんつーかもう、幅固定ってなんでこんなことしたいの? なんで Web デザイナはみんな画面サイズが大きくなったら当然ウィンドウサイズも大きくなってるものと決めてかかってくるの? なんで? さらに情報量増やしたいからフォントサイズも小さくしてさ。もうこういうのやめにしようよ。見にくいし使いにくいって。

one liner, one linerer, one linerist

one liner という言葉がある。日本語で一行野郎などと訳されたりするけど、要は一行スクリプトのことで、プロンプトにカタカタと打ち込んでいき、一度エンターを押すだけで結果が返ってくるように書かれたものだ。

さて。

では「one liner 使い」はどう呼ぶのがいいのだろうか。one linerer? one linerist? 達人レベルなら one liner wizard っていうのもアリかもしれないけど、下手の横好きの場合は one liner lover? うーん。

ほんとは one line script 使いが one liner なんじゃないのかなぁ。

About

例によって個人のなんちゃらです