Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

goog.Promise の使い方

日本語情報少ないので。

Promiseとは?

こちらをご覧ください。

Promiseオブジェクトを作る

newを使ってコンストラクタを呼ぶだけです。このとき、引数に関数をひとつ渡してやる必要があります。この関数はresolve関数とreject関数を受け取るので、関数内で

  • 処理成功時は resolve
  • 処理失敗時は reject

を呼んでやる必要があります。それぞれ引数に値をひとつ渡すことができ、他の処理に結果を受け渡すことができます。

var promise = new goog.Promise(function (resolve, reject) {
  if (...) {
    resolve(result);
  } else {
    reject(err);
  }
});

コンストラクタ引数は無名関数ではなく、メソッドあるいはローカルな関数として定義しても良いですね。公式ドキュメントではresolverと命名されています。

function resolver(resolve, reject) {
  if (...) {
    resolve(result);
  } else {
    reject(err);
  }
}

var promise = new goog.Promise(resolver);

成功時の処理を書く

var promise = new goog.Promise(resolver);
promise.then(function(result) {
  // resolve() の引数に渡した値をここで使える
  console.log(result);
});

失敗時の処理を書く

var promise = new goog.Promise(resolver);
promise.thenCatch(function(err) {
  // reject() の引数に渡した値をここで使える
  console.log(err);
});

必ず実行したい処理を書く

var promise = new goog.Promise(resolver);
promise.thenAlways(function() {
  // リソース解放など
});

合わせ技

then などのメソッドは自分自身を戻り値として返すので、メソッドチェーンが可能です。

var promise = new goog.Promise(resolver)
  .then(displayDialog)
  .thenCatch(displayAlert)
  .thenAlways(unlockSubmitButton);

Promiseの合成

静的メソッドとして、以下3種類が用意されています。

  • goog.Promise.all(promises)
  • goog.Promise.firstFulfilled(promises)
  • goog.Promise.rase(promises)

allはすべてresolveされた時のみresolveされるものです。thenにはすべてのPromiseの結果が配列に入れられて返ります。必要なリソースを並列で用意するときに利用できるでしょう。ES6のやつと同じです。

goog.Promise.all(assetLoader, dataLoader, calculator)
  .then(function(result) {
    var asserts = result[0];
    var data = result[1];
    var computed = result[2];
    doFoo(asserts, data, computed);
  });

firstFullfilledは、最初にresolveされた結果を返します。ひとつでもresolveされたら合成後のPromiseはresolvedになります。すべてrejectされたときのみrejectとなり、thenCatchが呼び出されます。同じ資源を複数の経路で準備できる際など、ひとつでも成功すれば良い・最も早く取得できた結果を使えれば良い時に利用できるでしょう。

goog.Promise.firstFullfilled(loadDataFromCDN, loadDataFromServer)
  .then(function(data) {
    doFoo(data);
  });

raseは最初にresolve/rejectされたPromiseの結果を返します。ES6のやつと同じです。

goog.resultとgoog.async.Deferred

これらは非推奨と明記されています。新規の利用は控えましょう。

  • NOTE: goog.result is soft deprecated - we expect to replace this and
  • {@link goog.async.Deferred} with {@link goog.Promise}.

Tips to accelerate your original game

To make your game more interesting, you should pay much attention to keep high performance. This is UX problem, but it is not designer who can solve. Only developer has solution for this problem, so we should learn about when we face problem and how many approaches we have.

Reducing I/O

Cache

This is popular tip for background-image, map-chip and character-chip.

Load picture and draw it onto cache on RAM, then you do not have to access to storage. For instance, your STG may load background-image when player decided the stage to play. By this cache, your STG does not have to read storage for background image during this stage.

Lazy-loading

In previous case, your STG needs to lock screen while it loads resources onto RAM*1. It's better to shorten this time, or player will feel that your game is boring.

To reduce locking, lazy-loading can be solution. At initialising phase, your STG does not have to load background-image of boss-stage. Postpone loading, then you can shorten the lock.

Pre-loading

Here is another and opposite solution. While player is choosing stage to play, your STG can load resources onto RAM. When player decides stage, necessary data is already loaded so you can start game immediately for player.

To use this tip, programmer should understand player's need and how they control your game. For instance, when player enters stage-select page, your STG may load SE (Sound Effect) of shooting action because you knows that player will choose stage so your STG should load such resources later. I think this is similar to implementing recommendation engine.

Are you implementing HTML5 game? If so, you may try pre-fetch API.

Reuse drawn picture

During user plays your STG, you should draw background-image and scroll it from right to left. So in each frame you should draw large picture, it costs resource of player's computer.

To reduce this cost, you can reuse background-image which is already drawn. You shift background-image some pixels, and draw only the right part. You can reuse almost all of drawn picture, and focus on drawing new parts.

Here is another example: even though you are implementing 3D game, you do not have to compute 3D models in each frame, because you can store computed images to RAM and use it instead.

Put multiple resource into one file

This is popular tip for map-chip and character-chip. And this is also very famous for web developer as CSS Sprites. Data URI also can be solution.

By this tip, you can reduce number of resource to load. So it's effective especially when resource is far from player. Learn latency for detail.

Compress data

You may like raw data format like BMP format, but when you publish your product it's better to use compressed format. Then you can reduce data size to load, so it will reduce time to load. Learn Throughput for detail.

To compress data, your may use ZIP or other format. It's OK but please remember that decompression costs machine resource.

Reducing computing cost

divide main-loop and loop-to-render

When you implement graphical game, you should have main-loop like below:

function main(){
  compute();
  render();
  setTimeout(main, 15); // about 60 FPS
}
main();

It works, but it has one problem.

This code tries to render image 66.6 times per second, but in my environment 6 of them has no meaning because we render twice or more between 2 display refresh. You can try it at this page.

It means that we render needless picture 6 times per second... it should be stopped.

var previousTimeToRender = 0;
function tryToRender(){
  var now = Date.now();
  if (now - previousTimeToRender > 1000/60) {
    previousTimeToRender = now;
    draw();
  }
  requestAnimationFrame(tryToRender);
};

function main(){
  compute();
  setTimeout(main, 1000/60);
}

main();
requestAnimationFrame(tryToRender);

Looks too complex? Then capsule it:

function Renderer(fps) {
  this.fps_ = fps;
  this.previousTimeToRender_ = 0;
  this.callback_ = null;
};
Renderer.prototype.tick = function(){
  var now = Date.now();
  if (now - this.previousTimeToRender_ > 1000/this.fps_) {
    this.previousTimeToRender_ = now;
    this.callback_();
  }
  requestAnimationFrame(this.tick.bind(this));
};
Renderer.prototype.register = function(callback){
  this.callback_ = callback;
  this.tick();
};

// then you can code like below:
function main(){
  compute();
  setTimeout(main, 1000/60);
}
main();
var renderer = new Renderer(60);
renderer.register(render);

And if we can reduce FPS to draw without UX problem, it's also good way to improve performance.

var renderer = new Renderer(30); // draw 30 times per seconds. we can keep computing 60 times per second.

Algorithm

Algorithm is very important to implement game. Routing, Z-sort, collision detection, AI... there are a lot of algorithm used in game. Brute-force is acceptable during development, but before you publish your game, it's better to consider to replace it with efficiently implementation.

This is too large topic for this article, so I just list up sample codes:

Now I'm interested in Simulated Annealing, I'll try to code a simple one.

*1:you may know that some PSP games display 'LOADING...' again and again

Intel Edisonを購入した

Intel Edison Breakout Board Kitを淘宝网にて515人民元で購入。当然ながら技適マークは無いです。

まずは以下を試験実施。

wget, python, node が動くことを確認。

翌日起動したところ *** Ready to receive application *** から進まなくなった(U-Bootが起動しなくなった)のでここを参考に再インストール。

$ brew install dfu-util coreutils gnu-getopt
$ cd edison-image-rel1-maint-rel1-ww42-14
$ ./flashall.sh
Using U-Boot target: edison-blank
Now waiting for dfu device 8087:0a99
Please plug and reboot the board
Timed out while waiting for dfu device 8087:0a99
DEBUG: lsusb
./flashall.sh: line 77: lsusb: command not found
DEBUG: dfu-util -l
dfu-util 0.8

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to dfu-util@lists.gnumonks.org

Did you plug and reboot your board?
If yes, please try a recovery by calling this script with the --recovery option

DFUデバイスとして認識されていない。--recoveryはOSXで使えないので対応を考える。

Windowsマシンに切り替えて再接続すると、特に苦労なく2つのLEDが点灯し起動完了。OSXとの相性問題?flashall.shのサポート状況を見ても、開発にはLinuxWindowsを選択したほうが無難だろう。

と、思っていたらWindowsが起動しなくなった。ドライバ入れたから?OS再インストールが必要そう。OSXでは相変わらず。

Windowsは(MacBook ProのBootcampで動いているので)パラメータRAMのリセットを行うことでOSXが起動するように。念の為OSXを最新にあげて再挑戦したところ、とりあえずWindowsは起動した。USB接続で2つのLEDが点灯することも確認済み。

goog.ui.Componentの継承で気をつけるべきこと一覧

基本

インスタンスの状態

  • クラスフィールド(static変数)にインスタンスの状態を入れない。
  • インスタンスフィールドはprivateにする。
  • 親クラスのフィールドには触れない。

DOM要素の操作

  • DOM要素を指定する際にはIDではなくclassやカスタムdata属性などを使う。
    • IDは、コンポーネントが画面に常に一つしか存在しないことを保証できる時のみ利用する(1つの画面に2つ以上同じIDを置けないので)。
    • Closure Libraryはclassによる要素検索メソッドを多数提供しているので、classを使うことが望ましい。
  • 自分自身のDOM要素に含まれるDOM要素のみ触ることが望ましい。
    • goog.dom.getElementByClass() などgoog.dom名前空間にあるメソッドやDomHelperのメソッドよりも、 this.getElementsByClass() などgoog.ui.Componentに用意されたメソッドを使うことが望ましい。

各メソッドで気をつけること

コンストラクタ

  • 必ずgoog.base(this, opt_domHelper)する。DomHelperは省略して良いが、引数で受け取る癖をつけて統一感を持たせる。
  • この時点ではDocumentに入っていない状態かつ自分自身のDOM要素がない状態。DOMの更新は控える。
  • コンポーネントインスタンスは極力この時点で作成する。あるいはコンストラクタ引数として受け取る。
  • this.addChild(childComponent)は親子でライフサイクルを統一するためにも効果的。これによって自身がdisposeされたタイミングで自動的に子コンポーネントもdisposeされるようになる。何らかの理由によりthis.addChild()できない場合はthis.registerDisposable(anotherComponent)しておくと良い。
/**
 * @constructor
 * @extends {goog.ui.Component}
 * @param {goog.dom.DomHelper=} opt_domHelper
 */
my.ui.Component = function(opt_domHelper) {
  goog.base(this, opt_domHelper);
  this.addChild(new my.ui.ChildComponent(this.getDomHelper()));
};
goog.inherits(my.ui.Component, goog.ui.Component);

createDom()

  • 必ずthis.setElementInternal(element)する。
/** @override */
my.ui.Component.prototype.createDom = function() {
  var element = this.getDomHelper().createDom(goog.dom.TagName.DIV, { 'data-foo': 'bar' });
  this.setElementInternal(element);
};

canDecorate()

  • decorateできるDOM要素に制限がある場合は、ここでそのチェックを行う。
  • falseを返した時のエラーメッセージ(goog.ui.Component.Error.DECORATE_INVALID)は汎用的なものなので、decorateに適さない理由をデバッグログに出しておくと良い。
/** @override */
my.ui.Component.prototype.createDom = function(element) {
  return element.tagName === goog.dom.TagName.DIV;
};

decorateInternal()

  • 必ずthis.setElementInternal(element)する。またはgoog.base(this, 'decotateInternal', element)でも良いが、特に親クラス実装を呼び出す必要性はない。
/** @override */
my.ui.Component.prototype.decorateInternal = function(element) {
  this.setElementInternal(element);
};

enterDocument()

  • まずgoog.base(this, 'enterDocument')してから、自クラス固有の処理を行う。
  • goog.base(this, 'enterDocument')は必ず呼び出すこと。
  • イベントのlistenはgoog.events.listen()ではなくthis.getHandler().listen()によって行う。これによってexitDocument時に手動でunlistenをする必要がなくなる。
/** @override */
my.ui.Component.prototype.enterDocument = function() {
  goog.base(this, 'enterDocument');
  this.getHandler().listen(this.getElement(), goog.events.EventType.CLICK, function(event) { ... });
};

exitDocument()

  • まず自クラス固有の処理を行ってから、goog.base(this, 'exitDocument')を呼び出す。
  • goog.base(this, 'exitDocument')は必ず呼び出すこと。
  • enterDocument時に自身や他のオブジェクトに加えられた変更があれば、これを巻き戻す。ただしthis.getHandler().listen()で作成されたイベントハンドラは親クラス実装で自動的にunlistenされるので気にする必要はない。
/** @override */
my.ui.Component.prototype.exitDocument = function() {
  // do some operation if we need

  goog.base(this, 'exitDocument');
};

disposeInternal()

  • まず自クラス固有のクリーンアップ処理を行ってから、goog.base(this, 'disposeInternal') する。
    • 自クラス固有の処理を先に行うとdispose()実行時に、自クラス固有のdisposeInstance→自クラス固有のexitDocument→親クラスのexitDocument→親クラスのdisposeInstanceの順に時に処理が行われることになり、exitDocumentがdisposeInstanceに遅れて実行されてしまう。
    • 公式に推奨されている。
  • goog.base(this, 'disposeInternal')は必ず呼び出すこと。
  • インスタンスフィールドにある他のオブジェクトへの参照をnull代入あるいはdelete演算子により断ち切ること。
  • this.isDisposed()が真かどうかは気にしなくて良い(偽であることを前提にして良い)。dispose()がthis.isDisposed()が偽の時のみdisposeInternal()を呼び出してくれる。
/** @override */
my.ui.Component.prototype.disposeInternal = function() {
  goog.base(this, 'disposeInternal');

  // do some operation if we need
};

getContentElement()

  • コンポーネントを追加するためのDOM要素がthis.getElement()と異なる場合のみ、このメソッドをoverrideする。
/** @override */
my.ui.Component.prototype.getContentElement = function() {
  return this.getRequiredElementByClass('container');
};

継承してはいけないメソッド

  • setElementInternal()
    • JSDocに Considered protected and final と明記されている
  • dispose()
    • 代わりに disposeInternal() を継承する
  • decorate()
    • 代わりに decorateInternal() を継承する
  • render(), renderBefore()
    • 代わりに createDom() を継承する
  • その他、継承する必要がないもの(ドキュメントには明記されていないが、これら標準実装に手を加える必要性はないものと思われる)
    • getElement()
    • getElementBy...()
    • getParent()
    • getRequiredElementByClass()
    • wasDecorated()
    • コンポーネントを扱うメソッド全般

参考資料

  1. goog.ui.Component のはぐれかた
  2. http://docs.closure-library.googlecode.com/git/class_goog_ui_Component.html
  3. GitHub - google/closure-library: Google's common JavaScript library

memo: RabbitMQ

TODO

  • 2回以上実行される可能性(結果報告前にConsumerが落ちる,タイムアウト etc.)と、そのために必要な設計(INSERT処理のみにする=更新・インクリメント処理は避ける etc.)
  • キューを作り分ける意味とその狙い。例えば動的スケールするべきキュー(即実行すべきJob)と処理が遅延してもよいキュー(永続性を優先すべきJob)に分ける?

Reference

最近読んだマンガ

まさかの2巻発売ということで、早速Kindleで入手しました。シャーリーがカフェに行く話が一番お気に入りです。大人が仕事をしている姿というのは素晴らしいものです。

長時間かけて書かれた本ということで、絵柄がかなりばらついているのですが、それでも同じ作家さんによる一連のマンガとして楽しめるのが不思議です。

俗に言う日常系四コマなのでしょうが、主人公が喋らないことによって人間模様が濃く描写されている印象です。肩の力を抜いて読めます。

Helck 1 (裏少年サンデーコミックス)

Helck 1 (裏少年サンデーコミックス)

なんか久々にすごいのが来た気がします。オンラインで最初の数話と最新話が読めるのでとにかくお試しを。

中国では技術力のある国・先進的な国として、よくドイツの名前を目にします。しかし私はドイツの実際はほとんど知りません。また国際結婚固有の問題に共感するところもあり、両方の意味から楽しみに読みました。

読後にもうちょっと内容があってもいいのではと思いましたが、このページ数だと仕方ないのかもしれません。

孔明のヨメ。 (1) (まんがタイムコミックス)

孔明のヨメ。 (1) (まんがタイムコミックス)

どこまで史実でどこまでフィクションかわかりませんが、ひとつのマンガとして楽しんでいます。3巻4巻がまだ未入手なので近々。

最近読んだ本

今日は日曜日ですが中国では出勤日です。国慶節に向けて休日の移動が発生しているのですね。

さて最近読んだ本の紹介です。電子書籍中心の生活をしていますが、これらはまだ紙の本での入手でした。こういう本も電子化されると、私みたいに海外で暮らしている人には嬉しいのですが。

心の筋トレの話、モチベーションの話、ブランドとミッションの話が面白いです。チームと働く上でどうモチベーションを上げてもらうか・どういう発想を持ってもらうかというのが大切なのは常日頃実感していて、それが経営のレベルになってもそんなに変わらないのかなという印象を受けました。 あとはサービス業であっても経営者の仕事は環境づくり・気付きの機会づくりがほとんどなのだなぁと。店長や店員に動いてもらう・考えてもらうためにどう伝えるかに腐心する話とか。自分の場合だと、日本企業の持つイメージと自分の企業の実際のギャップをチームメイト(全員外国人)に認識してもらいつつ、自分の企業の特徴をうまく活かした自己成長を想像し目指してもらうことが第一の課題なわけですが、今悩んでいることが40年後にも活きるのだと思うとすごく良い成長の機会を与えられているのだなと実感します。

おいしい穀物の科学 (ブルーバックス)

おいしい穀物の科学 (ブルーバックス)

「コメは粒のまま食べるのに、コムギは粉にするのはなぜか?」なんて考えたこともなかったし、その裏に科学的合理的な理由があるとは!トウモロコシは実が包まれていて鳥害に強い上に運搬も楽だから山岳地帯にも向いているとか、普段目にしているものが長年選ばれてきた理由や人との関わりの中でどう姿を変えてきたかとか、雑学を超えた面白さがあります。

特に興味深かったのは、コメの利用には土器の誕生が強く関係しているという話。加熱にも食事にも必要ですものね。縄文時代から土器・青銅器・鉄器などの器・道具が発達してきていて、それが農耕の生産性に寄与してきたのは知っていましたが、そもそも植物の摂食利用に必要だったのですね。学校で歴史を学ぶときにこういうことを知っていると、なぜそんなに土器や貝塚について学ぶのかをもっと深く理解できたように思います。 他にも竹を使い捨て食器として使える(土器のような器を必要としない)地域があり特殊な文化が育ったとか、コメは集落を作りやすいとか、稲はなぜ田植えするのかとか、コムギを中心とするには石臼が必要で定住向けだとか、なるほどなぁと思わされるものが多かったです。

朝に効く薬膳 夜に効く薬膳

朝に効く薬膳 夜に効く薬膳

引き続き薬膳を学んでいますが、すごい単純に言うと旬のものをまるごと食べなさい(一物全体・身土不二)に集約されるようです。雨が多い時期には水分を出す食べ物が旬を迎えるし、乾燥する時期には喉や肺を潤す食べ物が市場に出回るので、適宜それを取りなさいねと。 喉が乾燥していた時にレストランでなんとなく梨のジュースを選んで、あとで梨の効能を調べてふーんと思う時もありましたので、食べ物の効能については知識以前に本能や経験のレベルで理解しているのかもしれません。

健康状態の診断と改善に五行とか気血水とか考えられるようになるとまた違ってくるのでしょうが、そこまで大きく体調をくずすこともなく健康に暮らせているので、しばらくは活用の機会はなさそうです。

詳説日本史研究

詳説日本史研究

教科書ですね。オビに「大学受験に必携」って書いてあります。

海外で暮らして「外国人」の女性と結婚して、やっぱり日本人であることや日本の歴史を見なおす機会は増えています。最近興味深かったのは、日本では「終戦の日」「原爆の日」として認識・報道している日が中国では「ポツダム宣言の日」として報道されていたことで、当然ながら立場によって見方も認識も違うのだなぁということを再認識した次第です。

この本では日本の歴史について再度学んでいて、鎖国時の出島の役割について奥さんに話す際に参照したりしました。頭から読み進めるというよりは、適宜参照する使い方をしています。

日本の起源 (atプラス叢書05)

日本の起源 (atプラス叢書05)

まだ頭しか読んでませんが、天皇の起源や権力者同士の争いについて様々な説が紹介され楽しめています。「穀物の科学」で読んだコメの生産性が集約労働であがることやコストをかけるだけ収量を増やせることと合わせて読むと、当時の権力者の闘争にリアリティを感じることができます。

あと歴史の専門家が話しあうという形をとっていて、資料や説から一歩引いてそれらを俯瞰する立場で話が展開されているのが素敵です。歴史は勝者によって都合が良いように書き換えられるそうですが、そうしてできた資料をただ紹介するだけでなく「これこれこういう背景があったからこういう書き方をしているのかもね」という話をしてくれるので、知識のない素人でもいろんな見方が楽しめます。