Promise.reject()で処理は止まるがそのタイミングは「期待」通りとは限らない

まとめ

止まることは止まるが、思ったタイミングで止まるかどうかは分からないので、それ前提で作る必要がある。

例えば以下は思い違い。

  • 最初の一つが rejected になったら、その一つめで必ず止まる

実際には

  • 最初の rejected が Promise.all に伝わる段階ですでにいくつも並列で処理が走っている可能性があるので、rejected 以降にエラーがバンバン発生することはあり得る

もっと言うと rejected が伝わった段階で中の複数の Promise の結果は fulfilled と rejected が混ざっている可能性もあるので、全体の rejected を retry すると fulfiled になった Promise をもう一度実行する場合もある。

ということは例えば何かの POST 処理を Promise.all で回しつつ retry を考慮している場合、すでに成功しているデータが複数回 POST される可能性がある。

もっと言うと、

そこで何かの拍子に unique 制約に引っかかって逆にエラーがあとから起きたりもする。

ということ。

非同期並列面倒くせぇ…。

何で悩んでいたのか

Promise.all(Array.map(e => Promise))

なコードがあった。これを呼び出す function foo() があった。

function foo(data) {
  Promise.all(data.map(e => Promise))
}

この foo() を直接読んでテストしている時は最初の一つが reject になった段階で止まっていた。で、

一つめが rejected になったら止まると思い込んでしまった

しかし、次に

function bar() {
  ..
  ..
  foo()
  .then(() => {
    ..
  })
  .catch((err) => {
    ..
  })
  ..
}

の bar() でテストすると、

大量にエラーが起きてしまったので、「止まらないじゃん!」と、また勘違い

「何かコードの書き方を間違えているから止まらないのでは…」とありもしない原因を探る旅に出てしまったのでした…。

正解は、Promise.all([Promise, ..]) の実行に至るまでに払っている実行コストの間で中の Promise が並列に実行を開始してしまい、結果、

呼び出し元が rejected でストップしたタイミング以降にいくつもエラーが起きて当然の状況になる

つまり

一つめの rejected で止まるのも、まったく止まっていないように見えるのも、どちらも正しい!!!

非同期並列難しい!

何が難しいって同期的な処理を期待しているシステムと繋げるのが難しい!

愚痴まじりで言うと Node.js をこれまで避けてたのはやっぱり正しかったなぁと思った。それは動作が安定しているかどうかとか、高速かどうかとか、そういうことじゃなくて動作の考え方そのものが難しいので、カジュアルな変更を前提にしたコードには向いていないなぁという意味。記法だけを見れば callback 地獄は Promise によって解決されたかのように見えるが、動作については非同期並列という特徴はそのままなので、伝統的な、同期的で排他ロック可能なコードを扱う脳との切り替えおよび整合性の確保が難しい。整合性の確保は複数のサービスの連携が前提となるマイクロサービスにおいては必須の要件になってくる。

今後 FaaS, サーバレス化を進めるに当たってはここはキモになってくるし、

  • スケール、非同期前提のインフラに載せつつ変更の少ない部分に使っていく
  • 同期的な仕組みの側も非同期並列の仕組みから繋げる際のエラーの起き方を知っている必要がある
  • モニタリングの意味も同期的な仕組みとちょっと変わってくる

かなぁということを思っている。

だいぶ git つかめてきた

と言っても push とか pull とかしたいわけじゃなくて、

svn に突っ込む前のものを手元でだけ管理したい

という特殊な使い方をしているんだけど。というわけで push も pull も branch も merge も使ってません。要は現代的な RCS としてしか使ってない。

ちなみに個人的な好みでは Mercurial の方が好きだったんだけど、

  • Mercurial はバージョンと Python のモジュール絡みかなんかで、locale の設定がどうたらウザイことをぬかす輩がいた(要するにデフォルトのまますぐに使い始められないときがあった。たぶん PCC Mac だから。)
  • リポジトリに突っ込んでいない大量のファイルがあるディレクトリで使おうとすると圧倒的に git の方が Mercurial より速い
  • github を使えないとちょっと恥ずかしい

という理由で git も覚えなきゃ、と思って使っているのが現状である。積極的に使いたいわけでもないし svn との併用を前提に考えているので、git のいちいち操作性が微妙に違うところがヒジョーに気になっていたんだけど、だいぶ慣れてきた。最近便利に使っているのは以下のコマンドかな。

git commit -a
デフォルトでは add したもの以外は commit されない。
git diff [–name-only|–name-status]
svn のときは svn diff | awk '/^Index/ {print $NF}' ってやってたんだけど、git では必要なかった。
git log –name-status
これいい。変更のあったファイルの一覧が取れる。どのタイミングでどのファイルをいじったか分かる。patch の内容までは要らないけどいつ何をいじったか知りたいって場合は多いので。svn でもできるのかもしれないけど、svn の場合は Trac 任せなので細かい機能を知らないんだよな。
git log -n NUM
何個 log を表示するか
export GIT_PAGER=''
git は出力が長いときに自分で pager に処理を渡しちゃうんだけど、扱っているファイルのエンコードが Terminal のそれと違う場合とか化けてうざい。そこで pager の設定を空にしてやると pager に渡すタイミングを自分で制御できる。

まだよく分かってなくてちょこちょこ困るのは svn status -v 相当の情報の取り方。要するにどのファイルがいま変更とか追加されていて、それが commit される前の状態なのか、リポジトリに突っ込んでいないファイルを含めて一覧する方法。

個別には git diff –name-status とか git ls-files とか使ってなんとかほしい情報は取れるんだけど、一発でズバッと取れないかなーと思っている。なんでこういうことを感じるかっていうと、リポジトリに突っ込むつもりではいるんだけど、まだ突っ込んでいないファイルを一生懸命いじっていることが svn の場合はちょこちょこあって、それの確認に便利だからだと思う。git で手元のファイルを管理する分には必要になりそうなものは片っ端から突っ込んでいけばいいじゃん、というのがたぶん正解なんだろうなー。

ちなみに今は git メインでは使っていないので svn にコミットして一段落したら .git は消してしまっている。基本的にリポジトリは remote に置いておく方が安心だし、git master は今のところ存在しないので。

Reminderfox のポップアップがあんまり目立たない

せっかく使い始めた Reminderfox なんだけど、通知の方法が地味。ステータスバーとかツールバーの上にちょこっと文字が出る。ポップアップもなんか普通にウィンドウが開くだけで何の動きもないし、裏に回りっぱなしだったり、expose して切り替えてると全然目立たない。当然音は切ってあるので、音が出ても意味がない。

だからと言ってモーダルなダイアログで UI の制御をぶんどられるとそれはそれですごいむかつくんだろうけど、この状態はさすがに目立たなすぎてイマイチな気がする。

どうしたものか。

Yahoo! Widgets が面白いかも

突然デスクトップの話が増え始めたが、まぁサーバの話に行ったりデスクトップの話にきたりをくり返していた方がバランスがよかろうと。

で、なんで Yahoo! Widgets なのかというと、昔からクロスプラットフォームで動く小物デスクトップツールがほしいというか、やっぱ作れるといいなと思うことがよくあったから。

こういう小物は自分向けとか分かってる人が使う分には sh + awk とか Perl とか Ruby で書きゃいいんだけど、他の人も使えるとお互いにハッピーだよなと思うことは実はよくある。しかし awk とか Perl とか Ruby なんてのは普通の人の使っている PC(Windows)には入ってないし、OSX の場合は入ってるけど Terminal でコマンド叩けというのもアレなので、ちょっとした GUI がほしいという話になるわけだ。今なら Web ベースでもそれなりの使い勝手が実現できるので全部 Web ベースでもいんじゃね?と思ってしまいがちだが、現実にはローカルのファイルを操作したいとか、そういう需要が案外あって Web ベースには持っていけなかったりする。

プラットフォームを絞れるならこういう話に使えるツールは昔からある。AppleScript もそうだし、古くは DOS のバッチ用のユーティリティやメニュー系のツールも、HSP なんかもこういう系統に入れてしまっていいように思う。猛者になると Lotus 1-2-3 のマクロや Excel VBA でなんでもこなしてしまう人が出てくる、ああいう領域の話ね。今なら Windows でいちばんまっとうな手法は WSH なんだろな。たぶん。

問題はクロスプラットフォームって話になると途端に難しくなってしまう点で。いやね、

Windows で動かないとお仕事的には嬉しくないし、かといって Mac で動かないと自分が楽しくない

わけ。ここポイントね。テストに出すから。

つーことで以前から目をつけているのは wxWidgets や Gecko Runtime Environment だったり、まぁもっと言えば Java で書けばええやんという話になるわけだけど、やりたいことはそんなもんでイチからチマチマ書くような大げさなもんじゃなくて、ほんと sh スクリプトくらいなもんなわけ。(さすがに sh で書くのはちょっとしんどいけど。)

そこで Yahoo! Widgets なんだけど、これは内部に基本的な Unix コマンドを持ってるじゃないの。OSX は標準で入ってるから当然なんだけど、Windows 版も何十個もバイナリが入ってる。細かく確認したわけじゃないけど、Windows 版にバンドルされてるのは gnuwin32 のバイナリかな。たぶん。ま、つまり普通にローカルの作業ができるわけ。Perl や Ruby こそ入ってないけど、sh + fileutils + gawk ってのは普通にイケる。で、それに GUI を被せることができる。

いいじゃないの。

よおし。なんか書いてみっか。

ネットワーク認証型コピーコントロールCD “レーベルゲートCD”仕様の終了について

先に発表した Avex と違ってこっちは全廃なんですな。まーなんにしても消費者(とプレイヤー)に無用の負担を掛ける規格がなくなるのはよいことです。たぶん海外からの圧力なんだろうなぁ。SME は Avex よりも海外に強そうだし(印象)。日本で具体的な抗議行動みたいなのってあったのかな? ネット上にはその手のバナーはあったけど、そんなに有名なアクションて聞いたことないな。

実は WinMX や Winny の流通量減少を把握したうえでの決断だったりして。

About

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