LoginSignup
8
3

More than 3 years have passed since last update.

LiveScriptでasync/await

Last updated at Posted at 2018-04-25

【祝】LiveScript1.6が無事リリース

下記のような回避策は一切無しでasync/await使い放題になってます。
安心してご利用ください。

$ npm install -g livescript
/Users/miyabi/.nodebrew/node/v11.1.0/bin/lsc -> /Users/miyabi/.nodebrew/node/v11.1.0/lib/node_modules/livescript/bin/lsc
+ livescript@1.6.0
added 2 packages from 10 contributors, removed 1 package and updated 2 packages in 0.8s

$ lsc -v
LiveScript version 1.6.0

ご挨拶

全国推定50人のLiveScriptのファンの皆様こんにちは。
まだLiveScript使ってますか?

2018/04/24現在、まだLiveScriptのバージョンは1.5.0から更新されていません。
1.5.0はES7で実装されたasync/awaitに対応しておらず、Promiseを上手に扱う事が出来ません。

そこで、今回はGitHub環境に上がっている開発中のバージョンを導入して、
async/awaitを有効化してしまおう!という事を紹介していきます。

LiveScriptでasync/awaitが使いたければこれで導入すると良い

GitHubのプロジェクトに1.6.0 release planning #1016というIssueが建っており、そこで紹介されているインストール方法です。

# 何故かoptionatorとsource-mapも一緒に入れる必要があったので記載
$ npm install -g github:gkz/LiveScript optionator source-map

$ cat test.ls
get-user = ->> name: \taro
main = ->> console.log await get-user!
main!

$ lsc -bc test.ls
// Generated by LiveScript 1.5.0
var getUser, main;
getUser = async function(){
  return {
    name: 'taro'
  };
};
main = async function(){
  return console.log((await getUser()));
};
main();

$ lsc test.ls
{ name: 'taro' }

インストールは成功し、->>でasyncの関数を生成出来ているのが確認出来ますね。
確かにconsole.logの中身が二重括弧になってたりとちょっと微妙ですが、実行には支障がなく十分使える範囲と言えるでしょう。

因みにそのIssue (2018/3/18)ではそろそろ1.6出したいからスケジュール決めようと話しているようです。
しかし、開発者は「品質がまだまだ低くて自信を持ってリリース出来ない、使いたい人はこの方法でインストールしてみて」と仰っているようです。

ネイティブJSに戻った私の日記

私は業務で素のNode.jsを書いていたので暫く離れていました。

最初は「えー、今更ネイティブかよ…」と嫌々書いてたんですけど、ES6で追加された構文が本当にパワフルで、プチCoffeeScriptだと思いながらわりと快適に書くことが出来ました。
更にRamda.jsがプロジェクトで使えたので併用することで表現力の少なさによる不満も解消でき、非常に満足です。

そして、私はasync/awaitの便利さに目覚めました。
関数とか作らずにPromiseにパコッとawaitを被せるだけで、
Promiseの状態がpendingから変化するまで一生待って、値を取り出してくれるとか強すぎでしょう。

長らく構文がES5のままでオワコンと言われていた
CoffeeScriptも後追いですが2.0になり多くのES6やES7の武器を取り込み復活を遂げました
async/awaitももちろん使えるようになっています。
Ramda.jsさえあればカリー化もポイントフリースタイルも余裕でパワフルな表現力が出せます。

意外かもしれませんが、CoffeeScriptに慣れ親しんでいた皆さん、Ramda.jsとセットで戻ってみて下さい。
CoffeeScriptで括弧を作らずR.pipeで繋げて行く感覚は本当にヤバいです。

後必須級と言えるのはパイプライン演算子だけでしょう。
ライバルのTypeScriptはもう対応しているらしいので、CoffeeScriptも実装してどうぞ。

ES6登場後のLiveScript

ES6で追加された機能は沢山ありますが、
どれもこれもLiveScriptからすれば「何?今更俺の良さに気づいて真似したくなった???」といったレベルです。

久々に触りましたが、表現力の凄まじさは健在でした。
ES6のコードから更に半分以下に収まりますからね…
プライベートで書くコードは基本的に全てLiveScriptでやっていこうと思います。
これが私にとってのLISPです!

アロー関数が使えませんが昔ながらのthis束縛で十分です。
再代入禁止のconstもネイティブJSでは必須ですが、
LiveScriptでは行数が半分以下に抑えられてコードがスッキリしていますし、変数を大量に宣言する用事もないので困りません。

ES7登場後のLiveScript

ところが、そんなLiveScriptにも表現仕切れないものが出来てしまいました。
その筆頭がES7のasync/awaitです!

get-user = ->
  new Promise (resolve, reject)-> resolve name: \taro, score: 80
main = ->
  user <- get-user!.then
  user.score |> console.log

ご覧のようにLiveScriptにはバックコールというチートな構文があり、そこまで表現力は落ちません。
しかし問題はrejectを返された時で、エラーハンドリングが出来なくなります。

ナンテコッタイ
仕方ないので普通にthenやcatchで繋ぐ方式に…
丸括弧地獄にはならないのでネイティブと比べるとかなりマシですけどね。

get-user = ->
  new Promise (resolve, reject)-> resolve name: \taro, score: 80
main = ->
  get-user!
    .then -> it.score |> console.log
    .catch -> it |> console.error

しかし、流行りの非同期処理ライブラリを使い倒すようなシステムの場合、
大抵がPromiseを返す設計なので、thenを大量に重ねるようなケースではCoffeeScriptやネイティブのJavaScriptに表現力で劣るケースがあります。

これはまずい……

LiveScriptにもasync/awaitを…

LiveScriptくん、そろそろ更新してくれてもいいんじゃないかい?
npmのLiveScriptのページを見に行くと、貫禄のlast publish: 4 years ago

どう見てもオワコン言語です。本当にありがとうございました。
これ以外の機能は依然AltJSの中でもトップクラスだと胸を張って言える程なんだけどなぁ…

という訳で何とかならないか調査です。
ES7 async/await implementation with concise syntax #836

どうやらプルリクは全てmasterにマージされており、使う事ができそうですね。
ではちょっとLiveScriptの公式サイトで試してみましょう。
ペチペチ…「Compile」ボタンをポチっ!

get-user1 = (id)->> name: \taro
get-user2 = async (id)-> name: \taro
async function get-user3 (id)
  name: \taro
// Parse error on line 1: Unexpected 'COMPARE'

全然ダメじゃないか!!

async/await入りLiveScriptのインストール方法

まぁまぁ、masterにマージされているのは確実なんで、
masterブランチのものをnpmで使えば使えそうです。

冒頭ではIssueにあったものを採用しましたが、GitHubから直接ダウンロードして導入してもいけます。

$ git clone https://github.com/gkz/LiveScript.git
$ cd LiveScript
$ npm install

# -g付き名前無しでインストールすると今のものがグローバル領域に行く、自作のスクリプトをコマンド化したいときとかに便利
$ npm install -g

# 一度プロジェクトの外に出てからtest.lsを作ってみる
$ cd ../
$ vim test.ls
$ cat test.ls
get-user = ->> name: \taro
main = ->> console.log await get-user!
main!

# 実行!
$ lsc test.ls
{ name: 'taro' }

動きましたね!

構文紹介: async

まず、async/awaitIsuueで紹介されていた構文を試してみます。

get-user1 = (id)->> name: \taro
get-user2 = async (id)-> name: \taro
async function get-user3 (id)
  name: \taro

コンパイルするとこのファイルに

// Generated by LiveScript 1.5.0
var getUser1, getUser2;
getUser1 = async function(id){
  return {
    name: 'taro'
  };
};
getUser2 = async(function(id){
  return {
    name: 'taro'
  };
});
async function getUser3(id){
  return {
    name: 'taro'
  };
}

なんかダメな子がいますね、2番目の構文が非対応のままです。
その理由はこちらのコメントにあります。

summivox commented on 26 Oct 2016

The proposed syntax for LSC does not conflict with existing code, and is intended for early adopters specifically targeting ES7 (and quite possibly including further transpiling toolchain)

意訳するとasyncというキーワードは普通の関数適用に使っている既存コードがあるからだそうで、
後方互換を意識して->>async functionと明示した時だけ利用可能にしたいという趣旨の事を言っているようです。
更に追っていくと2番目の書き方はremoveということで取り除かれて使えなくなった事が分かります。

確かにnpmにasyncライブラリありますからね。
こちらは単なる関数なのでasync/awaitとは完全に別物です。
従来のコールバック関数を渡す処理ではこのasyncライブラリは非常に強力なので、
私は別にこれ使えばいいじゃん、無理にPromiseに変換する必要ないとまで思ってます。

構文紹介: await

もう見たまんまです。
ネイティブと同じなんで特に言うことはありません。

get-user = ->> name: \taro
main = ->> console.log await get-user!
main!

しかし、awaitは完璧とは言えません。
2018/04/25現在、Issueで漁るとレアケース下で不具合が残っているようです。

両方要するにLiveScriptのlet構文が原因ですね。
LiveScriptはES5時代に登場した言語で、letは関数ブロックを作る事で対処していたようです。

let, New
let is short for (function(a){...}.call(this, b)).

ところがyieldawaitキーワードは特殊な関数の配下でしか使えないワードです。
従って、letを使う際は親の関数が、function, async function, function*の3通りで切り替えなければなりませんが、
その切替がどうも上手く動作していないようですね。

for..letとか誰が使うんだよと思ってましたが、
awaitを軸に据える場合、ループ処理では使う事になりそうなので気をつける必要がありますね。

まとめ

いかがでしたでしょうか。
これを機に最強のAltJSであるLiveScriptで再始動してみるもの良いかもしれません。

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3