OpenCV + Google Cloud Vision API + Intel Edison で笑った瞬間を撮るカメラを作る

やりたいこと

最近娘が生まれて二ヶ月経過し、そろそろ笑ったりするようになりました。今回のテーマは娘が笑った瞬間を逃さずにカメラで撮影する事です。ちなみにこういう子どもをネタにして行うハック、僕はこれを『親バカハック』と呼んでます。

f:id:yosuke_furukawa:20160608084338p:plain

TL; DR

  1. Intel Edison でカメラをセット、一定のタイミングで撮影しつつ
  2. OpenCV で粗く笑顔認識させてから
  3. Google Cloud Vision API で表情解析
  4. 笑顔だと判定された画像を Slack で飛ばして画像をいつでも見れるようにする。

f:id:yosuke_furukawa:20160608102239p:plain

かわいい笑顔が撮れたので最高でした。

ハードウェアセットアップ

Intel Edisonを手に入れたのでそれを使って作ります。Edison は Arduino 拡張ボードなら普通のUSB web camera 対応しているので、それをただぶっさして使います。

f:id:yosuke_furukawa:20160608084105p:plain

Intel Edison はSDカードほど小さくて、 x86Intel Atom というプロセッサーを積んでます。あんまり詳しくないのですが、筆者が大学の頃、太古の昔の教科書では x86CISC系は組み込み系に弱くて、 ARM の RISC系のが組み込みに有利という認識だったのですが、どうやら時代は変わって今ならどっちもどっちでx86なのに組み込み系に乗る時代になったようです。

ともかく、 Edison にしたのはただ『そこにあったから』です。Noderとしては Intel Edison を初期化した時に最初からnode.js v0.12 が入ってるので嬉しい(頼むからv4+にしてほしい)。本当は Intel Edison じゃなくても良くて、一番試してみたかったのは Tessel 2 なんですが、日本で手に入れるのは難しいという話だったので、一旦 Edison でトライ。

Edison がうまくつながっていれば USB web camera をただ USB port にさすだけで使えるのですが、初期の頃はさしただけで使えなくてあれこれ調べて SW1 スイッチを USB 側にしないと使えないようです(ハマりどころ)。

dev.classmethod.jp

ちなみにほぼこれを参考にすればハードウェアのセットアップは終わります。

github.com

笑顔認識

顔画像を認識させるのは OpenCV で簡単にできるんですが、さらに笑顔認識までしようとすると、多少の小細工が必要です。 OpenCVCascade Classifier という機能を持っています。これは名前の通り、 分類器(classifier)を連結(cascade)させて特定するという機能です。笑顔認識で言えば、

  1. 顔画像領域を特定(画像の中から眼や鼻や口といった特徴のある領域を抜き出)し

f:id:yosuke_furukawa:20160608105656p:plain

  1. その顔画像領域の中で笑顔かどうか(顔の下半分に半月/三日月形の領域があるか)を特定する

f:id:yosuke_furukawa:20160608105722p:plain

という二段構えの分類機能の組み合わせで成立しています。 今回の OpenCV でやっているのは非常に単純かつ強力な仕組みで、2001年くらいの論文で解説されている Viola Jones Object Detection と呼ばれるものです。2001年の頃の論文が今ではライブラリとして簡単に扱えるので良い時代になったなと思ったのですが、残念ながらこの方法はそこまで精度が良くないです。構造が単純で解析に時間がかからないのが特徴です。

笑顔検出は npm モジュールから使えるようにライブラリにしています。

github.com

この smile-face-detector を使ってリアルタイムでデモする動画をとってみました。 ちなみに web 屋っぽく websocket で配信する仕組みです。

smile

github.com

Google Cloud Vision API を使って表情解析する

OpenCV で笑ったと判断したとしても、やっていることは『顔の下半分に半月形の領域があるかどうか』、なので本当に笑っているかどうかはまだ難しいです。そこは表情解析に定評のある Google Cloud Vision API というクラウドの力を借ります。

Google Cloud Vision API は表情解析、風景解析、画像内のOCR、画像内のオブジェクト認識ととんでもなく強力な機能を持った GoogleAPI です。これを使っただけでも何かできそうな気がしてきます。

Google Cloud Vision API は表情解析して、その表情から読み取れる感情が happy なのかそれとも sad なのか、はたまた surprised なのかといった結果を返してくれます。

http://cdn.mos.techradar.com/art/internet/Google/Cloud_Vision_API-970-80.jpg

ただやはりリアルタイムに解析するにはネットワーク通信のコストがかかるのとどうしても時間がかかる、あと、無料枠では1日1000リクエストまでなので、やたらめったらに送る訳にはいかないという制限があります。なので OpenCV での一旦フィルタを使って OpenCV で笑ってると判断された画像だけを Google Cloud Vision API に送るという仕組みにしています。

笑顔が検出されたら Slack に転送する

Google Cloud Vision API が happy だと判定してくれたら後は楽で、その画像を Slack に送ります。

f:id:yosuke_furukawa:20160608084338p:plain

github.com

外出中とか仕事中でもかわいい画像が見れて便利です。

はーかわいい

ただし

何日間か試して思いましたが、以下の点でこの仕組のままでは厳しいです。

1. Intel Edison がそこまで高速ではない

Intel Edison は 500MHz で dual core と組み込み系の中では高速ですが、さすがに1,2秒間隔で OpenCV を回し続けると不安定になります。 そこはやはり Raspberry PI 3 とかだと 1.2GHz quad core になるとのことなのでハードウェア側をもう少しCPUリソースが使えるものにする必要があります。もしくはC++とかで直接OpenCVを使うといいのかもしれません。

2. 首の座っていない赤ちゃんは顔画像特定が難しい

これはもうどうしようもないんですけど、まだ赤ちゃんの首が座ってないので OpenCV では正面をちゃんと向いていないと顔画像として認識してくれないので難しいです。なので顔が写ってても取りこぼすこともしばしば。これを解決するには画像を少しずつ角度を変えて実行する必要があるんですが、そうするとどうしてもさらに時間がかかってしまいます。

3. 親が笑ってると思ってもGoogle Cloud Vision API は笑ってないと判定する

自分から見ると笑ってて可愛い笑顔だなーと思って Google Cloud Vision API にかけると全くの「無表情」として帰ってきます。 Google Cloud Vision API が判定してくれるのはもっと分かりやすい笑顔なので、そこまではっきりとした笑顔になるためにはもう少し赤ちゃんの成長が必要です。

とはいえ

一旦丸3日位やってみたら、こういう可愛い笑顔が撮れました。

f:id:yosuke_furukawa:20160608102239p:plain

今はもう少し赤ちゃんの状況に合わせてリアルタイムに笑顔を撮るんじゃなくて、90秒間隔くらいで Google Cloud Vision API にかけながらSlackで様子を見てます。

f:id:yosuke_furukawa:20160608143537p:plain

まとめ

OpenCV + Google Cloud Vision API + Intel Edison で笑った瞬間を撮って、 Slack に送るカメラを作ってみました。 もう少し時間があったらEdisonじゃないハードウェアとかで試してみようかなと思います。

また Node.js で全部できそうだったので、Nodeで実現したけど、リソース効率を考えるともう少しシステムプログラミングよりの言語(CとかC++とかRust(?))を使ったほうがいいのかもしれません。

この辺も時間があったら試してみます。

ES Modules と Node.js について

書こう書こうと思いながらこのタイミングまでのがしてしまいました。 今一番 Node.js の中で hot な discussion の一つと言えるでしょう、『ES Modules が Node.js の中でどうなるか』です。

ES Modules 現況

ES2015 が発刊されてそろそろ一年です。 ES2015 にある機能は Node.js v6でも 93% 程度カバーされています。モダンブラウザでも大体が90%を超えています。しかし、 ES Modules だけはまだどのブラウザも実装しきれていません(kangax compat table は ES Modules は省かれてます)。

f:id:yosuke_furukawa:20160509010917p:plain

そもそも ECMAScript 2015 自身で定義されたのは構文だけなので、構文はともかく、どうやってモジュールを取ってくるかという Loader の部分がまだ決まりきっていません。

https://whatwg.github.io/loader/

現時点はいくつも決めなきゃいけないポイントがあって

  • 参照解決処理
  • 取得処理
  • script タグでどう書くのか
  • メモ化処理(所謂caching)

の全てを決めて一旦ロードマップ上のMilestone 0 が達成されるような状況です。

https://github.com/whatwg/loader/blob/master/roadmap.md

scriptタグでどう書くのか、参照解決処理など、ある程度決まっている処理はありますが、どの項目もまだ議論中です(少なくとも github 上ではまだ milestone 0 を discussion している最中に見える)。各種ブラウザでも、実装が始まっているところはありますが、仕様の方針待ちなところが多いです。

なんで Node.js に ES Modules が必要なのか

ES Modules の仕様が定義されるよりも前に Node.js は CommonJS と呼ばれるモジュールシステムを採用しました*1。それと npm というパッケージマネージャの組み合わせでエコシステムを作っています。結果として npm のエコシステムは Node.js にとどまらず、Browserify や webpack を組み合わせてフロントエンドにとっても大きなエコシステムになっています。

『CommonJS で既に育ってしまった生態系の中で ES Modules という標準仕様とどうやって相互運用性(interoperability)を取るのか』 これが ES Modules が定義され始めた最初からずっと Node.js / npm で語られてる事でした。

相互運用性がないとこれまでのエコシステムと乖離(friction)ができてしまいます。せっかく Browserify や webpack で埋めたfrontend browserとNode.js との乖離がこれでまた起きることになります。

Node.js ではじゃあどうしようとしているのか

これではイカン、という事で Bradley Meck 氏が interop を取ろうと Proposal を書き起こしました。最初に Proposal を書いた時は議論がいくつもあったので ものすごくたくさんの話が巻き起こってまとまらなかった のですが、何度も何度も議論を重ねて今やっと DRAFT というステータスになっています。

node-eps/002-es6-modules.md at master · nodejs/node-eps · GitHub

一応言っておくと DRAFT というのはやると決めた訳じゃないです。議論のテーブルに乗った状態DRAFT です。ステータスとしては DRAFT => ACCEPTDRAFT => REJECT の2通りがあるので REJECT されて無かったことになる可能性もあります。

ES Modules on Node.js 概要

おおまかな解決アルゴリズムを記述します。

まず、 DynamicModuleRecord と呼ばれる CommonJS 用の module 置き場を定義します。これは ES Modules から CommonJS の module.exports で定義されたものを import で読み込めるようにするためのレジストリです。

その上で下記のアルゴリズムで読み込みます。

1.  読み込もうとしているファイルが CommonJS で定義されているのか ES Modules で定義されているのかを確認する(※)
2. もし CommonJS なら
  2-1. ファイルを即時評価する(今まで通り)
  2-2. DynamicModuleRecord に `module.exports` で読み込んだものを入れる
3. もし ES Modules なら
  3-1. ファイルをパースする(import/export でファイルを取得して、bindingを作るため)
  3-2. 再帰的に全ての依存関係のあるファイルを持ってくる
  3-3. 全ての依存関係のファイルから `import`  の binding を作る
  3-4. 評価する

簡易フローチャートで書くとこうですね。

f:id:yosuke_furukawa:20160509230807p:plain

この後さらにケースとしてはファイルが循環参照されてたらどうするかとかの話がありますが、一旦そこは置いておきます。

読み込もうとしているファイルが CommonJS で定義されているのか ES Modules で定義されているのかを確認する ここが今のところ最大の議論のポイントです。

CommonJS なのか ES Modules なのかの確認方法ですが、読み込もうとしているファイルが .mjs拡張子だったら ES Modules、それ以外の .js 等であれば普通に CommonJS として判断しようとしています。

つまり、 CommonJS でも ES Modules でも両方共読み込ませたいモジュールを作る場合、 package.json に下記のように記述し、

{
  "name": "test",
  "version": "0.0.1",
  "description": "",
  "main": "./index", // 拡張子なしで定義する
}

index.mjsindex.js を定義します、片方には ES Modules 形式で書きます。

// index.mjs
export default class foo {
  //..
}

もう片方には CommonJS 形式で書きます。

// index.js
class foo {
  // ...
}
module.exports = foo;

こうすると読み込む側が .mjs 形式に対応している Node.js であれば、 先に .mjs で解決しに行きます。見つからなければ .js の方を解決する、という動きになります。

import で書く場合の path 解決方式

本筋からはそれますが、 import の重要なポイントなので記載しておきます。 ES Modules では Node.js が暗黙的にやっているようなスマートなパスの解決をしてくれない(現時点のローダーでは)ので気をつけましょう。

例えば、 require で書いた場合、 require('./foo') のように .js を削除して記載することが可能でした。

// ./foo.js を解決する
require('./foo');

import でモジュール参照解決をする場合、ES Modules の仕様としては暗黙的に .js を保管してくれたりしないので気をつけましょう。

// ./foo だけ参照解決する
// ./foo.js や ./foo.mjs や ./foo/index.js は参照解決しない
import './foo';

./foo のようにローカルパス付きで読み込む場合は必ず .js もしくは .mjs のように拡張子を付けて参照解決させる事になるでしょう。

import './foo.js';
import './bar.mjs';

ES Modules => CJS

これまでの説明だけでも分かりにくいと思うので例を上げて説明していきます。ここでは ES Modules から CommonJS で定義されたモジュールを読み込む場合です。ES Modules から CJS を「名前付きで」読み込んだ場合、 default というプロパティが入ります(これめっちゃ分かりにくい)。

// cjs.js
module.exports = {
  default:'my-default',
  thing:'stuff'
};
// es.mjs

// 名前付き(as baz)で ./cjs.js のファイルを import する
// bindings なので中の値は外から書き換えられない。
import * as baz from './cjs.js';
// baz = {
//   get default() {return module.exports;},
//   get thing() {return this.default.thing}.bind(baz)
// }
// console.log(baz.default.default); // my-default

// default のオブジェクトを import して foo にアサインする
import foo from './cjs.js';
// foo = {default:'my-default', thing:'stuff'};

// default プロパティを明示して読み込む
import {default as bar} from './cjs.js';
// bar = {default:'my-default', thing:'stuff'};

値を export して、 default の値としてアサインされる例:

// cjs.js
module.exports = null;
// es.mjs
import foo from './cjs.js';
// foo = null;

import * as bar from './cjs.js';
// bar = {default:null};

関数を export する例:

// cjs.js
module.exports = function two() {
  return 2;
};
// es.mjs
import foo from './cjs.js';
foo(); // 2

import * as bar from './cjs.js';
bar.name; // 'two' (関数名が取れる)
bar.default(); // 2 (default 関数に assign される)
bar(); // throws, bar is not a function

CJS => ES Modules

反対に CommonJS から ES Modules を読み込む時は下記のようになります。こちらは export default で export した場合は .default プロパティにアサインされます。

export default を利用する例:

// es.mjs
let foo = {bar:'my-default'};
// note:
export default foo;
foo = null; // これは import 側に影響しない、 export した時点の値が返る、何故なら binding じゃなくて値だから
// cjs.js
const es_namespace = require('./es');
// es_namespace ~= {
//   get default() {
//     return result_from_evaluating_foo;
//   }
// }
console.log(es_namespace.default);
// {bar:'my-default'}

export を利用する例:

// es.mjs
export let foo = {bar:'my-default'};
export {foo as bar};
export function f() {};
export class c {};
// cjs.js
const es_namespace = require('./es');
// es_namespace ~= {
//   get foo() {return foo;}
//   get bar() {return foo;}
//   get f() {return f;}
//   get c() {return c;}
// }

今のところの議論

ちょうど今盛り上がってるのには理由があって、仕様が DRAFT になって議論が始まった後に新しく Counter Proposal (反対提案) が書かれました。それが defense of dot js という Proposal です。

これは .mjs という拡張子で解決するのではなく、 package.json にフィールドを足すだけで解決させるようにしたいという Proposal です。

defense of dot js の内容

こちらの仕様では、基本的に完全な互換性を取るのは諦め、 ES Modules で読み込むことをベースとします。 package.jsonmain フィールドがある時だけはすべてのファイルが CommonJS で読み込まれます。これが基本的な仕様です。

明示的に ES Modules で読み込ませたければ package.jsonmodule フィールドでエントリーポイントを書きます。

// package.json
{
  "main": "index.js",
  "module": "module.js"
}

こうすると、 module フィールドがあれば Node.js は ES Module としてエントリポイントを見に行きます。古いバージョンの Node.js は main フィールドのエントリポイントを見るだけなので古いバージョンにも対応されます。

しかし、このやり方には問題が1つあります。 module 以外のエントリポイントを require から指定できません。例えば、 lodash とかでよく見る require('lodash/array') みたいな読み込み方ができません。そこでこれを解決するために modules.root というフィールドを利用します。

// package.json
{
  "main": "index.js",
  "module": "module.js",
  "modules.root": "lib"
}

上記のように"modules.root": "lib" フィールドがあると lib/* 以下を require から読み込めるようになります。つまり、 ES Modules で書いてあっても require('lodash/array') みたいな書き方ができるようになり、ある程度互換性を保てるようになります。

とはいえ、新しいバージョンで mainmodule も無い package.json では暗黙的には必ず ES Modules になってしまうので、かなり breaking changes です、その代わり拡張子での解決は要らないので、 .mjs などの拡張子を検討する必要はありません。なので、 "Defense of dot js" なわけです。

CommonJS と ES Modules 両方対応するなら?

基本的には ES Modules に傾けるのがこの仕様のポイントですが、人気のあるモジュールはそうはいきません。 ES Modules と CommonJS の両対応させる必要のあるモジュールは存在するでしょう。この時は諦めて transpile して、 ES Modules => CommonJS のファイルも用意しておきます。 transpile した JavaScript も一緒に package に入れておきます。 main フィールドと module フィールドを書いておけば古い Node.js と新しい Node.js で読み込み先を変えてくれます。

Bradley Meck仕様との違い

Defense of dot js 側は将来的に ES Modules に全て揃えよう という姿勢です。互換性をある程度損なっているし、それが起こす混乱はある程度受け入れる考えです。それに対して Bradley Meck 氏の仕様は互換性重視です。あくまで今のCommonJSと相互運用性を取るというのを主軸に添えて語られています。拡張子で ES ModulesなのかCommonJSなのかが変わるというのは言い換えれば一つ一つのファイル単位で ModuleなのかCommonJSなのかを切り替えられる柔軟な仕様です。過渡期は .mjs と .js が混じるかもしれませんが、将来的にユーザーの判断でどっちがデファクトになるかによっては .mjs だけ残る可能性はあります。

hard choice (.mjs vs package.json)

これに対してさらに Counter で Bradley Meck 氏はブログを書いています。

medium.com

なんで .mjs を選んだのか、という理由が書いてあります。 ブログの基本的な論調としてはみんな .js という拡張子に頼りすぎているという話です。

JavaScript には様々な"方言" があります。CommonJS, UMD, AMD、これらの全ての JavaScript は全て .js になってます。

ES Modules というのはもうそれ単体で評価可能な Script ではないし、パースして他のファイルをロードしてチェックするという処理が必要な以上、もういっそ拡張子ごと変えるというアイデアも分からなくはないです。しかも Module だと暗黙的に strict mode で動くので、普通のScript とは別物だと思ったほうがいいです。

他の言語の例を挙げるなら、 .plPerl スクリプト) と .pmPerl モジュール)のように拡張子を変える事を是とする文化もあります。また、ファイルを開く前に拡張子だけ見れば Module なのか Script なのかが分かるので、人間にとっても意味はあります。

この仕様はまだまだ議論中です。また今週の TSC ミーティングで議論があるでしょう。先週もありましたが、話はまとまりきらずに終わりました。今のうちに何かこうしたいという意志があれば issuePR に書くことをおすすめします。

まとめ

  • ES Modules の現時点の状況
  • ES Modules を Node.js はどうするのか
  • ES Modules on Node.js Proposalの詳細
  • Counter Proposal である Defense of dot js の話

*1:(正確には CommonJS の仕様からは大分外れてます。今やあれが CommonJS という事になっちゃってますが・・・)

Node.js v6.0 (Current) がリリースされました。

さて、とうとう皆さん待望の Node.js v6.0 がリリースされました!次のLTS候補です。LTSになるのは2016年の10月からの予定です。v6 の LTS 期間は明示化されてないですが、ルールに照らし合わせれば、LTSになってから 2年半がサポート期間なので、おそらく 2019年4月まではサポートされます。

Node v6.0.0 (Current) | Node.js

Node.js v6.0 の主な変更点

  • ES2015 support の改善
  • module load性能の改善
  • Buffer API の new Buffer() コンストラクタの廃止 (セキュリティ上の理由から)

ES2015 support の改善

やっぱりこれが一番大きな変化ですね。

node.green を見てもらえればわかるかもしれませんが、 ES2015 のサポートがこれまでは 58% だったのが 96% まで大幅に拡大されました。

f:id:yosuke_furukawa:20160427021442p:plain

細かいところはnode.greenを見てもらうとして、いくつかのデフォルトで有効になった機能を紹介します。

デフォルトパラメータ

これは、関数に引数を渡す際に渡されない引数(undefinedな引数)にはデフォルトで特定の値にしてもらう、という機能です

function foo(a = 1, b = 2) {
  return a === 3 && b === 2;
}

foo(3) // true
// 引数の値をデフォルト引数の値として使うことも可能
function f(list, indexA = 0, indexB = list.length) {
  return [list, indexA, indexB];
}
console.log(f([1,2,3]); // [1,2,3], 0, 3
console.log(f([1,2,3],1); // [1,2,3], 1, 3
console.log(f([1,2,3],1,2); // [1,2,3], 1, 2

デフォルトパラメータが書けるようになったので、いままでやっていたような もしも引数 a がundefinedだったらデフォルトで値をセットする という処理が読みやすく、書きやすくなりました。

デストラクチャ

デストラクチャリング、和訳すると分配束縛と呼ばれる機能です。Clojureにある機能ですね。 これを利用すると配列やオブジェクトで設定した値を取り出しやすくなります。 一番良く使うのは値をswapさせる時かと思います。

具体的には以下のとおり。

var hoge = 123;
var fuga =456;

// 値をswapする
var [fuga, hoge] = [hoge, fuga];

console.log(hoge); // 456
console.log(fuga); // 123

var [a, [b], [c], d] = ['hello', [', ', 'junk'], ['world']];

console.log(a + b + c); //hello, world (aに"hello", bに",", cに"world"が入ってる )

var pt = {x: 123, y: 444};
var {x, y} = pt;
console.log(x, y); // 123 444

Rest パラメータ

可変長パラメータを持つ関数を作る時に arguments を使わずに作れるようになりました。

// ...itemsでRestパラメータ
function push(array, ...items) {
  // それをarrayにpush (ここはspread operator)
  array.push(...items);
}
var list = [1, 2, 3];
//list変数に数字をpush
push(list, 4, 5, 6);
console.log(list); //[1, 2, 3, 4, 5, 6]

正規表現に sticky option と unicode option が付く

sticky option は直前に実行した正規表現のlastindexを覚えておき、次に検索する時はその lastindex から検索するというオプションです。

var text = "First line\nsecond line";
var regex = /(\S+) line\n?/y;

var match = regex.exec(text);
console.log(match[1]);  // "First"
console.log(regex.lastIndex); // 11

var match2 = regex.exec(text);
console.log(match2[1]); // "Second"
console.log(regex.lastIndex); // 22

unicode option は正規表現のマッチ時にユニコードリテラルを使って書けるようになったり、サロゲートペアにマッチできるようになるためのフラグです。

"𠮷".match(/^.$/u)[0].length === 2

Proxy API

Proxyが提供してくれるのは、"ちゃんとした" メタプログラミングです。Proxyに関しては、Brendan Eich の構想を描いた発表資料があります。

www.slideshare.net

これが 2010 年という事で、Node.jsが流行りはじめたくらいに提案されたというのが歴史の深さを物語っていますが、Proxyは割りと色んな事ができるAPIです。5年経て、上のスライドで紹介されているものとは API が異なりますが、以下のことに使えます。

  • Getter/Setter への インジェクションを行うことで、プロパティの変更や参照をされた時に処理を変えることができる。
  • method_missing みたいな存在しないメソッドを呼び出した時に呼ばれるメソッドを定義できる。

などなど、名前の通り、代理オブジェクトとして呼び出されたときの処理を肩代わりすることができます。

Proxy API で Getter/Setter にインジェクトする。

こんな感じのことができます。

var handler = {
    get: function(target, name){
        return name in target?
            target[name] :
            37;
    }
};

var p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;

console.log(p.a, p.b); // 1, undefined
console.log('c' in p, p.c); // false, 37

存在しないプロパティを呼び出した時のデフォルトの振る舞いを定義できる。

const obj = {abc: 123};
const PropertyChecker = new Proxy(obj, {
  get(target, propKey, receiver) {
    if (!(propKey in target)) {
      throw new ReferenceError('Unknown property: '+propKey);
    }
    return Reflect.get(target, propKey, receiver);
  }
});
console.log(PropertyChecker.abc); // 123
console.log(PropertyChecker.def); // Reference Error

これをうまく応用することで例えば method_missing 的な存在しないメソッドがあった時の振る舞いを定義できます。

Reflect API

一方で Reflect API は Proxy API と対になって使われることが多い API で、 Proxy が処理にインジェクトして、代理オブジェクトとして振る舞うのに対して、 Reflect API はそれのユーティリティオブジェクトで、対象となるオブジェクトに対して処理を反映(Reflect)させる動きをします。

最初にReflectを僕が知った時は対象となるオブジェクトが取れるならそれを直接変更すればいいので、いまいち使いドコロが分かってなかったんですが、ある程度使ってみると使い所が分かってきました。

使い所 その1 演算子じゃなくて、関数にしたい時

in 演算子を考えてみましょう

'assign' in Object // Object のプロパティに `assign` があるかどうか

これは演算子を使った式です、Reflect を使うと下記のように書くことができます。

Reflect.has(Object, 'assign'); // true

演算子じゃなくて関数で表現されているのがわかります。

他にも delete 演算子を Reflect を使って書き換えることが可能です。

// delete 演算子
var a = {abc: '123', def: '456'};
delete a['abc']; // {def: '456'}
// Reflectを使った場合
var a = {abc: '123', def: '456'};
Reflect.deleteProperty(a, 'abc'); // {def: '456'}

専用の演算子を使ったほうが短く書けますが、関数を使って代用できるようにしておくと読みやすさや反映する対象がわかりやすくなります。

使い所その2 例外よりも戻り値として扱いたい時

Object.defineProperty というObjectに対してプロパティを定義する専用の関数がありますが、これは defineProperty に失敗すると例外がthrowされます。そのため下記のように書かなくてはいけません。

try {
  Object.defineProperty(obj, name, desc);
  // 成功時の処理
} catch (e) {
  // 失敗時
}

Reflectができるようになると下記のように書けます

if (Reflect.defineProperty(obj, name, desc)) {
  // 成功時の処理
} else {
  // 失敗時の処理
}

--es_staging flag で有効になるもの

--es_staging flag を付けるとES2015の機能がさらに増えます。ただし、 --es_staging flag はまだ unstable の状態の機能を試すのに使うためのフラグなのであまり本番で積極的に使うものではありません。

末尾再帰呼び出し最適化

再帰呼び出しをした時に再起かどうかを内部的に検知して、再帰呼び出しを普通のループに変換します。 詳しくは下記の teppeis さんのブログが参考になります。

teppeis.hatenablog.com

そこから持ってきた下記のスクリプトで試します。

'use strict'; // 今のところ strict mode でのみ有効
function factorial(n, acc) {
  if (n <= 1) return acc;
  return factorial(n - 1, n * acc);
}

[0,1,2,3,4,5,10,100,1000,10000,100000].forEach(function(n) {console.log(n, factorial(n, 1))});

階上計算処理ですが、これを普通に実行すると、 100000 の所で stack size オーバーフローでエラーになります。

$ node fact.js

0 1
1 1
2 2
3 6
4 24
5 120
10 3628800
100 9.332621544394418e+157
1000 Infinity
10000 Infinity
/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:2
function factorial(n, acc) {
                  ^

RangeError: Maximum call stack size exceeded
    at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:2:19)
    at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10)
    at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10)
    at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10)
    at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10)
    at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10)
    at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10)
    at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10)
    at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10)
    at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10)

しかし、 --es_staging で実行すると通るようになります。

$ node --es_staging fact.js

0 1
1 1
2 2
3 6
4 24
5 120
10 3628800
100 9.332621544394418e+157
1000 Infinity
10000 Infinity
100000 Infinity

Function#name

今までは無名関数だと .name プロパティから関数名を取れませんでした、これが よくある無名関数を左辺に代入する書き方だと問題がありました。

// function.js
var abc = () => {};
const def = function(){};
console.log(abc.name); // abc
console.log(def.name); // def

// ただし、この場合は 普通に名前付き関数の名前が採用される

let ghi = funciton jkl() {};
console.log(ghi.name); //jkl
$ node function.js


jkl

これを --es_staging を付けると下記のようになります。

$ node --es_staging function.js
abc
def
jkl

逆に有効になっていないもの

一番有名所としては import/export でしょうか。 これに関しては、ただいま絶賛議論が紛糾している所です。

ES modules を Node に持ってくる、というのはかなり難しい問題で、今のところまだ有効な打開策が出ていません。そもそもまだ v8 でもパースできないのでパースできるようになったとして、どうやって解決するかという議論が始まったばかりです。

議論のまとめはこちら

github.com

module load 性能の向上

benchmark WG が継続してベンチマークを取るようにしてくれました。

Node.js Benchmarking

benchmark working group では requireや起動時間といった性能を計測するマイクロベンチと acmeairと呼ばれるチケット予約管理アプリを使ったマクロベンチの2種類を実行しています。 v4.x と比較した時に require  した時の性能が初期ロードで 3倍高速に、二回目以降のロード(cache付きロード)で5倍高速になるようになりました。

ops/sec higher is better f:id:yosuke_furukawa:20160427100426p:plain

また acmeair を使った全体のスループットに関しても若干の向上が見られます。

f:id:yosuke_furukawa:20160427101015p:plain

Buffer API の new Buffer() コンストラクタの廃止

セキュリティ上の理由から、 new Buffer() コンストラクタは廃止されました。 詳しくはこの辺りを見てもらうと良いかもしれません。

おまけ

Windows XP/Vista で node.js のサポートが終了しました。

まとめ

他にも色々と変更はありますが、全てを紹介しようとすると膨大な量なので一旦ここまでに停めておきます、すべての変更をちゃんと追いたい人は以下のChangelog を参考にしてください。

node/CHANGELOG.md at master · nodejs/node · GitHub

アップグレードするかどうか

まず v0.10 / v0.12 を使っている場合は今年の年末でサポートが切れてしまうので v4 もしくは v6 への upgradeを推奨します。

f:id:yosuke_furukawa:20160427102953p:plain

v4 を使っているユーザーは 2018年までサポートは続くのでまだアップグレードしなくても構いません、とはいえ上にあげたような新しい機能を使いたいユーザーはアップグレードを検討してください。

v5 を使っているユーザーはあと2ヶ月はサポートされますが、v5はLTS対象じゃないので、アップグレードを推奨します。v6 にアップグレードすることを検討してください。

Stable ラベルから Current ラベルへ

今までは Stable というラベルが最新のバージョンにはついていましたが、 LTS と混同されやすいのでラベル名が Current に変わりました。v6がLTSになったらその時はおそらく LTS ラベルが付き、 v7 が Current へと変わります。

v7 のリリースもまだ明示化されてないですが、今のところの計画では今から半年後の2016年の10月には次のv7.0がリリースされる予定です。

まとめ

Node.js v6.0 がリリースされました。

  • 主な変更点
  • ES2015 サポート拡充
  • 性能向上
  • new Buffer API の廃止
  • アップグレード検討有無
  • Stable から Current へ

という話をしました。

DeNAを卒業します

この週末にこの話を書こうと思っていたのですが、筆不精なもので土日を普通に過ごしてしまいました。

さて、表題の通りDeNAを卒業することになりました。

今回は技術的な話ではなく、単純にライフステージの変化の話なので気にならない人は読み飛ばしてください。

DeNAに入社したのは3年半前

yosuke-furukawa.hatenablog.com

だいぶ昔の記事ですね。この時は同年代や年下の若いエンジニア達がメキメキと力をつけていく中で「なんとかしなければ」という焦燥感で常に何かしらをウォッチしつつ、新しい技術があれば飛びついて調べ、アウトプットする、というようなことをやってました。(今でもアンテナは張っているつもりです)

そんな折に DeNA に誘われて入社できたのは非常にラッキーだったと思っています。新しい技術を積極的に採用しつつも、ある程度冷静かつ成熟した判断をしている部分があり、しかも事業としても色んな事を多方面でやっているので面白かった。

yosuke-furukawa.hatenablog.com

初めて入って、 Play Framework x Java で海外の人たちと英語でコミュニケーションしながら一緒に海外向けのゲームを作ったり、 PerlでHTMLもCSSもJSもほぼ全部一人でウェブサイト作ったり、 かと思ったら Node.js でリアルタイムゲームのR&Dをやったり、QuizNowっていうリアルタイムゲームを作ってYAPCで宣伝させてもらったりもしました。つい最近まではNBPFと呼ばれる新しいmobageのプラットフォーム周りの仕事をしてました。有意義なキャリア開発ができました。

DeNAという会社でもともとやりたかった下記のことはだいたい実現できました。

  • ウェブアプリの作り方の基礎を身につける
  • WebSocket等の新しいプロトコルでゲームを作る
  • JavaScript(Node.js)を使って仕事をする
  • 英語でコミュニケーションをする

次はどこに行くのか

あんまり隠してないので、言ってしまうとリクルートテクノロジーズっていうところに行きます。 Node.js をサービスでも使っているのと、 Node.js のOSS活動・コミュニティ活動を奨励する、という話だったので、OSS活動、コミュニティ活動も継続しようと思っています。特に Node.js committer/evangelism 活動はやり続けたいと思っています。

ただ誤解のないように言っておくと、 DeNA でもその手の活動は別に禁止されていません。むしろ奨励してくれています。

最近だとkazuho さんの h2oとかが良い例じゃないかなと思います。 他にもsonots さんみたいにRuby committer/Fluentd committerとしても活躍している例もあります。

じゃあなんで転職するのか

これには僕のライフステージが変化したのが大きいと思います。簡単に言ってしまうと、子どもが生まれました。 しばらくは子ども中心の生活になるのと後は自分のキャリアとか条件を鑑みて総合的に判断しました。

子どもが生まれるというのは非常に大きな変化でして、今は子どもにかかりきりです。ソフトウェアを作るのとは全然違った楽しさがあります。この記事も子どもが寝てくれたので隣で寝顔を見ながら書いてます。

f:id:yosuke_furukawa:20160418175847j:plain

しばらくは、OSS committerをしながら Node.js ユーザーグループ代表をしながら、サービスも作りながら、 Fulltime パパ committer をする予定です。

f:id:yosuke_furukawa:20160414143427j:plain

細かい話は飲みとかランチとかに行った時にでも話します。皆さん、よろしくお願いいたします。

謝辞

業務用アプリ作り続けて、ソーシャルゲームとかウェブアプリケーションのことを何も知らないで入ってきた門外漢に対して懇切丁寧に教えていただきありがとうございました。3年半という長いようで短い時間でしたが DeNA の皆さんと過ごした時間はかけがえの無いものでした。狭いIT業界なのでまたお会いすることもあるかと思いますが、その時はどうぞよろしくお願いいたします。

wishlist

一応置いておきます。(何か頂けるのであればベビー用品・生活家電系があると助かります)

www.amazon.co.jp

今後ともどうぞよろしくお願いいたします。

JavaScriptの文化とleftpadの話とpadStartについて

無駄にラノベみたいに長いタイトル書いちゃったんですが、まぁやっぱり一言くらいは残しておくかと思ったので書きます。長いのでまとめだけでも見てもらえると良いかもしれません。

leftpadの話はかなり大事になっていて、Node.js界隈を中心としてその他のOSSをやっている全体的に話が波及しています。幾つかの記事を読みました。今回はJSの文化と歴史についてちょっとずつ書いていこうかなと思います。

本の虫: npmからkikとその他諸々が消されたまとめ

江添さんの話はすごくよくまとまっていて、ネタも含めた上で一番面白い話になっていました、ここで言われている下記の疑問に答えていこうと思います。

もっと憂うべきパッケージがある。isArrayだ。このパッケージは一日88万回もダウンロードされていて、2016年2月だけの一ヶ月間に1800万回もダウンロードされていて、72個ものNPMパッケージが依存している。

isArrayの中身はこうだ。

var toString = {}.toString;

module.exports = Array.isArray || function (arr) {
  return toString.call(arr) == '[object Array]';
};

結局、このコードは本質的にたった一行のコードである。

その他、is-positive-integerという整数が正の整数かどうか判定するパッケージがあるが、これも本質的には4行のコードである。ところが、このパッケージは昨日まで3個もの依存を持っていた(今は0個になっている)

なぜこんなにも多数のパッケージに依存をするのだ?

さて、これは JavaScript エンジニアじゃなければ当然の疑問だと思う。
つまり、『isArray っていう一行のOSSのコードをロードするっていうのはなぜなのか』『なぜこんなにも多数のパッケージに依存するのか』である。

isArray のこれまで

さて、行数に注目する前にisArrayについて深掘りしてみましょう。

昔々に存在していた JavaScript GoodParts という古典がある、これについては今日の JavaScript ではほとんどが過去の knowhow になってしまっているが、昔を知る上では非常に重要だと思う。

www.oreilly.co.jp

JavaScript GoodParts にはなんと Array かどうかをチェックする『 is_array という処理はこう書け』という指南が付いている。
「6.5 配列かどうか」より抜粋

var is_array = function (value) {
    return value &&
        typeof value === 'object' &&
        typeof value.length === 'number' &&
        typeof value.splice === 'function' &&
        !(value.propertyIsEnumerable('length'));
};

つまり、値が存在し、値がtypeof で 'object' と判定され、 length というフィールドが数字であり、 splice メソッドを持っていて、 length が列挙可能なものであれば、それは配列であるというわけだ。ここまで判定しないとちゃんとした配列なのかどうかは 昔のJavaScriptでは分からなかった。

さて、この方法が編み出されてから少し時間が経過してさらにカジュアルなチェックが存在するようになった。それが上に貼っていた、Object.prototype.toString.callメソッドの内容で判定する方法だ。

var is_array = function (arr) {
  return Object.prototype.toString.call(arr) == '[object Array]';
};
is_array(['foo', 'bar', 'buz']); // true

さて、そもそも配列かどうかを判定するのにここまで必要な理由がわからないという指摘もあると思う。現代のブラウザでは上のようなチェックも既に過去のノウハウとなっている。今ECMAScript 5 が実装されているブラウザが手元にあれば、これだけで良い。

Array.isArray(['foo', 'bar', 'buz']); // true

JavaScript GoodParts が出版された頃のブラウザの世界では初期のようなチェックまでやらないといけなかったのかもしれないが、現代のブラウザではだいたいArray.isArrayでのチェックをしておけば大丈夫なはずだ。

JavaScriptでArrayの判定 - 思ったこと

isArray っていう一行のOSSのコードをロードするっていうのはなぜなのか

さて、話を元に戻そう、過去を振り返った結果として、最初は複数行あったコードが時代とともに洗練されていくのが分かったかと思う。実はここに『isArray っていう一行のOSSのコードをロードするっていうのはなぜなのか』と『なぜこんなにも多数のパッケージに依存するのか』の理由が隠れている。

JavaScriptの出自がWebブラウザで主に実装されている、という特性上、クロスブラウザでの対応が求められることが多い、その場合に Array.isArray だけでチェックしていた場合、古いブラウザでは動作しなくなってしまう

そこで、 isArray ライブラリの出番になる、Array.isArrayが存在するかどうかを調べて、無かったら内部的に Object.prototype.toString.call() でのチェックをする。こういう『foo機能が存在するかどうかを調べて無かったら内部的に代替手段を用意する』事を polyfillと呼んだりponyfillと言ったりする。polyfill/ponyfillの細かい定義はおそらく誰か別な人が書くと思うのでそちらに任せる。

レガシーブラウザも含めた広い環境で動作することが要求されるアプリケーションであったり、Node.js v0.10でも動くことを求められているライブラリで全て polyfill をわざわざ手書きしていたら生産性が落ちてしまうのは想像しやすいと思う。そこで OSSライブラリに頼ってなんとかするような状況になる。

この polyfill を使う文化 こそがJavaScriptOSSのコードをロードする文化であり、多数のパッケージに依存する原因である。

『たかだか1行とか11行の〜』という話だが、行数自体は関係なく、 polyfill/ponyfill を使って書いていたほうが全体的に整合が取りやすいことが多いから書いていることが多い。もちろん自分でpolyfillを書いたほうがコスト的に安いことも多いので、その場合はわざわざ OSS に頼ったりはしないが、ある程度成熟したpolyfillは自分で書くよりも実績があるのでそれを使っておく方が安心する事も多い。

また、 polyfill というのは言い換えれば『新しい関数が環境に行き渡ったら不要になる』ものだ。

いらなくなったら該当のpolyfillは削除できる。そうして徐々に新しい関数に適用していく事で今日のウェブは急に壊れることなく使えている

翻って leftpadの話

leftpadが unpublish された件 はコミュニケーションのミスや npm の仕様などの不幸が重なった結果発生した事故で、あんまり頻繁に起きるようなことじゃない、とはいえ、かなり大きなニュースになってしまった。もう npm からは今回の反省点も含めてちゃんと声明を出しているので個人的にはnpmの対応には納得している。

The npm Blog — kik, left-pad, and npm

『急に壊れることなく使えている』という話をしたが、今回は不幸が重なって一部のOSSが壊れてしまった。こういう事態を避けたかったら自前で書くほうが良いと言えるだろう、もっと言えば npm に限った話ではなく github であったり bitbucket であったりが急な不幸に見舞われることだって無いとはいえない、それをどうしても避けたいのであれば、もう自分で管理するしか無い気はする。OSSに依存するっていうのはそういう事だと割りきって使っている。

さて、leftpad も isArray と同じようなたった 11行のコードである、頑張れば1行でも書ける。

leftpad のコードがまずいとかそういう話で色々語られてはいたが、確かに愚直にループを回して足していくより今は String.prototype.repeat というメソッドがあるのでそれを使ったほうが綺麗にコードが書けるし、高速である。

paulownia.hatenablog.com

ただ String.prototype.repeat自体が ES2015から新しく定義された関数であり、leftpadのような幅広い環境で動かすような事を目的としたライブラリでは利用できない。最速な実装とは言えないが、単純に書くなら現状維持でも十分問題ないと思われる。

これも ES2015 が十分に行き渡れば中のコードを String.prototype.repeatで書き換えてしまって問題ないはずだ。

padStart の話

話を未来方向に向けるとpadStart/padEnd と呼ばれる仕様が ECMAScript で提案されている。実際のインタフェースはleftpadとは少し違うが下記のように書くことができる。

'hello'.padStart(10); // '     hello'

さらによいニュースとして、実際に v8 では実装が始まっている、ChromeやelectronやNode.jsではこれが使えるようになる事もそんなに先の話ではない。

https://chromium.googlesource.com/v8/v8/+/1a272ba23ec490f73349201c014537c851f3c964

つまりは leftpad のようなコードも時代とともにこう変わっていくだろう。

現在:

function leftpad (str, len, ch) {
  str = String(str);
  var i = -1;
  if (!ch && ch !== 0) ch = ' ';
  len = len - str.length;
  while (++i < len) {
    str = ch + str;
  }
  return str;
}
leftpad('hello', 10);

ちょっと先の未来

function leftpad (str, len, ch) {
  str = String(str);
  if (!ch && ch !== 0) ch = ' ';
  return String(ch).repeat(len - str.length) + str;
}
leftpad('hello', 10);

更に先の未来

'hello'.padStart(10);


さて、 leftpad を padStart の polyfill として見た場合には、 isArray と Array.isArray と同じ状況であることがわかると思う。この手の話はたくさんある。

僕はJavaScript というのは我々言語ユーザーと言語仕様策定者が歩みを寄せながら差を埋めつつ育てていくものだと思っている。polyfillだったり、痒いところに手が届くような小さいモジュールを作ってnpmに公開しておく行為は差を埋める一つの手段である。

そういうライブラリが浸透していくことで言語仕様策定者側はユーザーにとって必要な機能がわかる、ライブラリを元に仕様を決めていくための指針ができる。ライブラリを公開/利用するのは無理矢理言ってしまえば未来のJavaScriptへの貢献にもなっている。

まとめ

  • isArrayの歴史(Goodparts => Object.toString => Array.isArrayの変遷)
  • leftpad も実際はisArrayと同様
  • JavaScriptは言語ユーザーと言語使用者が歩みを寄せて差を埋めつつ育てる言語、polyfillもnpmへの公開もそれを使うのも、その差を埋めるための手段

Developers Summit 2016で非同期処理について話してきました

だいぶ間が空いてしまったんですが、デブサミ 2016 で非同期処理について話してきました。

event.shoeisha.jp

トピックとしてものすごく突出してたので成立するか不安だったのですが、なんとなく結論めいたものはでました。

詳しくは takezoe さんのブログを見て頂けると良いかと思います。

takezoe.hatenablog.com

結論みたいなもの

まぁやっぱり同期処理と非同期処理は『意識して使い分けなくても言語なりフレームワークなりで吸収するようになっていくんじゃないか』というのが1つの結論のような形になった。ES.Nextの async/await はその1つの形だと思う。"意識しないで"というのは難しいが、ほとんど同期処理と変わらない形で書けるようになる。

Node.js は coreの中でも Promise を採用するように只今構想中だし、ユーザーランドの場合はPromiseからの async/await への移行はそこまで難しくないように思える。

ただし、async/awaitやPromiseは 所謂単発モノと言われる、一回呼んだら非同期で返ってくるのが基本の処理だ。これでほとんどの処理はまかなえるかもしれないが、一回呼んだだけでは終わらないものも中にはある、時系列的に連続で発生する系の非同期イベントだ。

この『時系列的に連続で発生する類のもの』をどうやって扱うのかというのの1つの解が Stream のような data を chunk として扱って連続的に read / write するタイプの API だったり、 さらにそれを抽象化した Rx と呼ばれるライブラリだったりするんだろうと思う。

Stream については Chrome の開発をしている Jake がわかりやすい図を書いてくれているので引用する。

jakearchibald.com



Without streamingProcessRenderFetchWith streaming

この図はfetchの処理をする時にいっぺんに取得する時とchunk化して少しずつ取得する時の動きの違いをvisualizationしたものだが、わかりやすい違いが見えてるんじゃないかなと思う。

こういう処理をするような場合には非同期処理はさらに一歩先に進み、同期処理とは一線を画するモノになっていくと思う。単発モノの非同期処理を非同期処理レベル1とすると、こういう連続的なイベントを扱わなくてはいけないような非同期処理は非同期処理レベル2とも言える。

レベル1の非同期処理は同期処理とは変わらない形で書けるようになっていくとは思うが、レベル2の非同期処理はそもそも扱っているものの性質がこれまでの同期処理とは違うので『違いを意識しないで書く』というのは難しい気がする。

ただ幸いなことに現実的に今のサーバーサイドではそこまで連続的なイベントを扱うような事は経験上まだ少ない。僕が経験した所で言うと、 socket.io を使ったゲームだったり、いわゆるリアルタイムなウェブアプリを書くっていうような処理をした時に経験したくらいだ。

『サーバーサイドでは』と言及したが、クライアント側では割と頻繁に起きていると思う。クライアント側は非同期処理が複雑化しやすく、例えば Ctrl-x, Ctrl-s で保存するタイプのmarkdown editor を書きたいと思ったらそこそこ複雑なイベントハンドリングを求められる。ただの click イベントで発火するだけの処理を書いてる状況ではなくなっている。

そういう意味ではサーバー側もクライアント側も今後アプリケーションが複雑化してくる事を見越して非同期処理を意識して書いてみるのも良いのではないだろうか。

会場から来た質問

Q. 非同期処理と例外はどうしたらいいのか
A. JavaScript に関して言えば、今はPromiseにくるんで返すというのが一般的になっている、callback の第一引数に error を返してた時代から Promise の catch 関数でキャッチできるようになる時代はもう今来ている。更に進んで async/await になれば try/catch で例外を処理できる時代が来る、そこまで来ればあまり困らないはず。ただし、連続したイベントを扱うようなケースはまたちょっと趣が異なる。

Q. 非同期処理が廃れて同期処理だけで書ける未来は来ないのか
A. なくならないと思われる、昨今のアプリケーションでは外部APIを叩く場合やDBにアクセスする、S3にアクセスするなどのネットワーク経由で処理する事が多い。そのネットワークを経由する際にレイテンシが発生する。そうするとそのレイテンシを愚直に待つよりも非同期処理にして別タスクを実行するほうが効率が良いし、このレイテンシはどんなに速くなったとしても光の速さを超えられないので、どうしても厳しい。

最後にNode.jsの話を

非同期処理がわかりにくいとかそういう話は色々あると思いますが、一回Node.jsやってみるとわりとわかりやすくて良いんじゃないかなと自分では思っていますステマとかじゃなくて、非同期処理しかないという環境に入れられると否応なくその書き方に慣れます。callback地獄とか言われてるような処理に陥らないように工夫すると上で書いたような話もわかってくるんじゃないかなと。

なので、非同期処理を学びたければ、ぜひ一度Node.jsを!!

2015年のふりかえり

さて、もう一個の記事です。

本当この一年は振り返っておかないともったいない一年だったので振り返っておきます。

もう2016年になっちゃったけどスルーします。僕が認めるまで2015年です。

考えてみると、Node.js日本ユーザーグループ代表になって2年目です。当初あげていた目標である、海外に向けての意見発信であったり、国際カンファレンスでの発表経験だったり、YAPCやCROSSでの講演と本当に色々経験させてもらいました。

1月

◆ CROSS 2015 で講演(OSSという枠でパネルディスカッション)

2015.cross-party.com


◆ io.js 出たばかりで issue 追ったり PR 出したりしてました。
当初の目的は io.js と Node.js の混乱を少なくとも国内だけは抑えられるようにしようと思っていました。

2月

◆ 東京Node学園祭で io.js について発表

◆ Node.js v0.12 が出たのでサマリをまとめる。
yosuke-furukawa.hatenablog.com

3月

◆ io.js Evangelism WG に入る。この頃から翻訳活動だけじゃなくて、io.js weekly reportをたくさんするようになる。

4月

◆ Hexi をリリースして、 http2study で発表するなど。

◆ NodeSchool Tokyo を主催する。
yosuke-furukawa.hatenablog.com

◆ TowerOfBabel を作って、400 star 稼ぐ。
yosuke-furukawa.hatenablog.com

◆ React meetup で mithril とか mercuryの話をする

5月

◆ isomorphic meetup を開催。
nodejs.connpass.com


◆ io.js v2.0 をまとめつつ、 io.js Collaborator にもなる。
yosuke-furukawa.hatenablog.com

◆ NodeSchool International Day を主催する。
nodejs.connpass.com

6月

◆ ES2015 が制定。 ES6 meetupを開催。
nodejs.connpass.com


◆ StrongModeについて発表

◆ NodeConf Adventure に参加。
yosuke-furukawa.hatenablog.com
yosuke-furukawa.hatenablog.com


7月

gloopsさんの勉強会で Future of Node について発表、

8月

YAPC ASIA で Node.js と io.js の発表をする。

個人的にはかなり大きいカンファレンスでの発表だったので嬉しかった。これまでYAPCは見る側だったので感慨深いです。

◆ io.js v3.0.0 リリースをまとめて解説
yosuke-furukawa.hatenablog.com

◆ io.js collaborators meetup in San Francisco に参加してました。


9月

◆ NodeConfEUで発表してきたり!

yosuke-furukawa.hatenablog.com

◆ ISUCON5 参加 アンド 惨敗
yosuke-furukawa.hatenablog.com

◆ Node.js v4.0 リリースそしてまとめ!
yosuke-furukawa.hatenablog.com

10月

◆ Node.js v4.0 の話を 東京 Node 学園 18時限目で発表しました。

11月

東京Node学園祭2015を開催しました。
yosuke-furukawa.hatenablog.com

nodefest.jp

このために半年くらいかけてたので、みんな楽しんでくれて本当報われました。

◆ enjapanさんで国内外で活躍するエンジニア向けに発表してきました。

12月

◆ GTUGGirls に呼ばれて講師をしてきました。
gtuggirls.connpass.com

electronica を作ったのもこの時でした。


◆ NodeSchool Tokyo in leverages
nodejs.connpass.com

◆ JSおじさんで発表してました。

まとめ

総括すると、本当に色々経験させてもらった一年でした。この一年で得たものは大きかったので次の年は絶対にこれを更に大きな経験にしたいと思
います。特に海外での発表経験はすごく良かったので来年も継続していきたいと思います。そのためには再度発表できるクオリティの物を仕込めるようにしていきたいと思います。

また仕事的にも面白い事がたくさんできるようになってきているので、どんどん発表していきますね。

2016年もよろしくお願い致します!!