coffee + lodashでやらかす凡ミス
class MyClass hide: -> @visible = false
みたいなクラス作って lodash.eachで
_.each instances, (i) -> i.hide()
みたいな回し方すると全部回らなくて死ぬ
なぜかconsole.logすると動くみたいな闇な感じになって死ぬ
Google Chart API で QRコード作れる
しらなかった〜
手続きを意識させない設計をする
Twitter のツイート数を取得できる URL がある。
http://urls.api.twitter.com/1/urls/count.json?url=example.org
例えば、この URL からツイート数をコンソールに表示するプログラムを書きたいとする。coffeescript で
愚直に書くとこんな感じ
JSON_URL = "http://urls.api.twitter.com/1/urls/count.json?url=" request = require "request" request JSON_URL+"example.org", (err, res, body) -> data = JSON.parse body console.log data.count
これだと使いまわせないから、関数にする。
getTweetCount = (url, callback) -> request JSON_URL+url, (err, res, body) -> data = JSON.parse body callback data.count getTweetCount "example.org", (count) -> console.log count
良さ気に見える。
でも、これだと、複数回よんだとき、
同じ URL でも何度も読みに行ってしまうから、良くない。
getTweetCount "example.org", (count) -> console.log "first", count getTweetCount "example.org", (count) -> console.log "second", count
もちろん、これぐらい近い位置で呼ぶなら、
同じコールバックで括ればいいけど、そうも行かないことは、ままある。
ここで満を持して、クラスやイベントを活用する。
予め、load をしておいて、値を取得するときは、 get を使う
EventEmitter = (require "eventemitter2").EventEmitter2 class TweetCount extends EventEmitter constructor: (@url) -> load: => request JSON_URL+@url, @onLoad onLoad: (err, res, body) => data = JSON.parse body @count = data.count @emit "load" get: -> @count tweetCount = new TweetCount "example.org" tweetCount.load() tweetCount.on "load", -> console.log "first", tweetCount.get() tweetCount.on "load", -> console.log "second", tweetCount.get()
しかし、これだと、
load 前と load 後で、値の呼び方を変えなければいけない。
load 後は .on "load" が呼ばれないし、load 前は @count が定義されていない。
使うときには、TweetCount が既にロードされているかどうかを、常に意識しなければいけない。
そういう事のないように、
複雑な手続きを踏むインターフェイスは、
順番や状態を意識せずに使えるよう、善く設計する必要がある。
class TweetCount extends EventEmitter constructor: (@url) -> @loaded = false @loading = false get: (callback) => return callback @count if @loaded @once "load", callback @load() unless @loading load: => @loading = true request JSON_URL+@url, @onLoad onLoad: (err, res, body) => data = JSON.parse body @count = data.count @loading = false @loaded = true @emit "load", @count tweetCount = new TweetCount "example.org" tweetCount.get (count) -> console.log "first", count tweetCount.get (count) -> console.log "second", count
状態を吸収することで、
常に同じインターフェイスを利用できるように、設計をする。
co と jQuery 組み合わせて動くのでは
var co = require("co"); var $title = $("#title"); co(function*(){ yield $title.animate({fontSize: 100}).promise(); console.log("1"); yield $title.animate({fontSize: 10}).promise(); console.log("2"); yield $title.animate({fontSize: 200}).promise(); console.log("3"); yield $title.animate({fontSize: 20}).promise(); console.log("4"); yield $title.animate({fontSize: 400}).promise(); console.log("5"); yield $title.animate({fontSize: 0}).promise(); console.log("end"); });
6to5 --blacklist generators hoge.js > temp.js | browserify temp.js | regenerator --include-runtime > fuga.js
async 捨てられるのでは
我々の求めた ES6 Generators と Arrow functions
動くじゃないか!
let gen = function*(resume){ let wait1000ms = () => setTimeout(resume, 1000); yield wait1000ms(); document.write("Hello<br>"); yield wait1000ms(); document.write("ES6<br>"); yield wait1000ms(); document.write("Generators<br>"); yield wait1000ms(); document.write("Click Somewhere<br>"); yield document.addEventListener("click", resume); document.write("Well done<br>"); }; let it = gen(x => it.next(x).value); it.next();
6to5 --blacklist generators hoge.js | regenerator --include-runtime > fuga.js
なおlet