以下は Promise 全般について詳しい者が書いているわけではなく、単に JavaScript の実装に基づいて分かったことを書いている程度の話です。
※ ただし、Promise.all() や Promise.race() を catch から書くのはいろいろまずそうなのでやめた方がよいです。
Promiseについて説明されないこと「catchが何をcatchしているのか」
少なくとも JavaScript の実装では
- Promise が reject したもの
- catch 以前に Promise call 後に throw された例外
の両方を catch してしまう。
何が困るのか
特にサンプルとして世の中に出てくるコードの大半は残念ながら
Promise
.then(func)
.catch(func)
の形をしている。ここに罠がある。具体的には
then の中の例外がすべて catch に吸い込まれてしまう。
この Promise はこういう挙動をするはずだ、例えば「Fetch API の catch はネットワークエラーに対応しているはずだ」という理解でコードを書き始めるとしょっちゅう意図しない catch が起き、then を途中ですっ飛ばしてしまうという挙動に悩まされる。しかも console には何も出ない。
これは以下のような簡単なコードで確認できる。
fetch('/success', {method: 'POST'}).
then((response) => {
throw new Error();
console.log(response)
}).
catch((err) => {
console.log(err)
console.log('network error')
})
ネットワークエラーが起きていないにも関わらず network error と console には表示される。(この場合はその前に throw new した Error も表示される。)
まずcatchしろ
上に対する解決策は単純で、
「まず reject を catch で処理しろ、そのあとに then を書け」
になる。こうすれば then の中のミスを問題なく普通に runtime に伝えることができる。
※ はじめ「さもなくば捨てろ」と書いたが、Promise の引数は resolve, reject の順で必須だし、Node.js 8 では catch がないと UnhandledPromiseRejectionWarning が出まくるので勧めない。
Promiseは本当に面倒くさいと思った、
Promise は callback 地獄に対する回答のように見えるが、
- 値の Proxy(取得タイミングはお任せ)
- Promise をもとに「処理を記述する」
の複数の役割を持っており、単なる Proxy なら単に await すれば値が取得できるだけのように見えるが、Promise ベースで catch/then で「状態を待ち、処理を記述していく」際には、「状態こそ不変かもしれないが Promise の reject もその他の例外も『記述順によっては丸ごと catch される』」という繊細な挙動をする」。
この辺が実際にものを作っていく際に本当に難しいなぁというか、異常に面倒くさい感じがしましたとさ。
どうしても then の中が複雑になる場合はそこだけ切り出して別な名前空間で書いた方がいいんでしょうね。渡ってくる response が何かは分かってるわけで、テストに十分なデータのパターンを作って Promise の外で普通に TDD で作ります、自分なら。
おしまい。
参考
いや知らなかった。
$ ruby --help
Usage: ruby [switches] [--] [programfile] [arguments]
-0[octal] specify record separator (\0, if no argument)
-a autosplit mode with -n or -p (splits $_ into $F)
-c check syntax only
-Cdirectory cd to directory, before executing your script
-d set debugging flags (set $DEBUG to true)
-e 'command' one line of script. Several -e's allowed. Omit [programfile]
-Fpattern split() pattern for autosplit (-a)
(snip
「awk でもいいんだけど Ruby が標準で持ってるメソッドを使えばもっと楽にできる処理」がたぶんあったんだと思うけど、なんか急に Ruby の autosplit を使って処理してみようと思い立った。1
で、使ってみたら split された結果がどこに入っているのか分からなくて困った。awk の代わりに使おうと思っているのでてっきり split した結果は $1, $2, $3, … に入る、
help の $F という表記はそういう意味なんだと勝手に思い込んでしまっていた。
つまり、F は 1, 2, 3, … に置き換えられるものなんだと。でも違った。
本当に $F という変数ができていて、しかも zero origin の Array だった。
まぁ確かに split は split であって back reference じゃないし、String#split の挙動と合っているので自然なんだけどね。awk脳の恐怖っ。
※ その後、結局 autosplit はやめて普通に自前で回して自前で split して自前で処理した。
実際にこの日記を書いているのは2週間近く経ってからなので、理由はもう覚えていない ↩
今日の twitter
00:36:30 <mumumu> migration53.xml を訳していると、俺みたいな馬鹿でも頭
悪いなって思う変更が数点あるけど、楽しいところも沢山
あることがわかってきた。
http://www.mumumu.org/~mumumu/phpmanual/migration53.html
06:37:07 >wtnabe< お。PHP 5.3 で is_a() は deprecated ではなくなったの
か。よかったよかった。もっとちゃんと is-a の意味を考
えるべき。
- Twitter / Yoshinari Takaoka: migration53.xml を訳していると、俺み …
- Twitter / wtnabe: お。PHP 5.3 で is_a() は depre …
この点に関して自分は以前、以下のように書いている。
14:55:45 >wtnabe< PHP のオブジェクトのクラスを確認する構文が is_a() 関
数から instanceof にしたいみたいだけど、これ、あんま
よくない気がする。
14:56:37 >wtnabe< is_a っていう文字を見なくなると is_a 関係への意識が薄
れると思う
オブジェクトが is-a 関係かどうか
というのは常にきちんと意識できた方がよいと思う。この意味がピンと来ないならばオブジェクト指向への愛が足りない。とか断言できるとかっこいいんだろうな。実際にはよく知らない。
でも以前、明らかに継承すべきでなかったクラスを継承してコードを書いていた頃は、確かに「is-a 関係」を今ほど意識できていなかった。今は反省している。マジで。
バッテリパック以外の一部部品を交換したので、実は新品の方が安いってことになっちゃったんだけど、まぁ古くなったやつを処分しちゃうことを思えば処分代も掛からないし、えせエコロな感じで自己満足。
これから何回か満充電と放電をくり返さなきゃならない。意外と面倒。
- ペラペラ
- nikkei bp 朝刊/夕刊
- Weekly Mag2
- impress direct
をどうにか配信停止に。でも逆に nikkei のメルマガ増やしてしまった。まぁ頻度は落ちるのでよしとしよう。
あと振り分けルールを増やしまくってみたが、しかし実際のところメールシステムは何にするのがいいのかなぁ。
アップル、Rendezvousをアップデート–WindowsやLinuxなどにも対応 (CNET Japan)
マジかー!
モジラ、オペラ、アップルらが提携–ActiveXに対抗する新技術開発へ (CNET Japan)
いい話っぽいのであとでじっくりと(?)
標準の CVS プラグインでは CVS のパスワード認証しか使えなかったけど、これなら鍵認証が使える。よさげ。
げと。
T/O
from セキュリティホール memo
自分とこのプレスリリースなのに、「ソフォス」ってつけるんだなぁ。
まぁ OS X なら Clam が完全に動くんですが。
便利だな、Sage
うーん。やっぱ自分はもっと日本語読むべきなんだろうか? あうあう。
フツー困るよね。つーかこの Dan Kogai って人がもう少し考えて実装してくれたらよかったんじゃ。
です!
Mozilla 1.4 を立ち上げてみただけなのですが、気持ち速いですね。Duron 800MHz だと一応体感できます。
しかし、Netscape のサイトで気になる記述が…。現在
Windows | 7.1 |
Linux | 現在、7.02 のみ |
Mac OS X | 現在、7.02 のみ |
Mac OS Classic(OS 8 & 9) | 7.02 のみ |
という書き方なのですよ。分かります? MacOS Classic には「現在」という文字がないのです。これって…。
Netscape 7.1 も使ってみました。比較のために Netscape 7.02 をしばらく触って、それから 7.1 へ。
はえーーーーー!
そりゃそうだ。Netscape 7.02 って Mozilla 1.02 ベースだもんね(-_-; でもこれだけ速くなればもう十分普通の人でも違和感ないでしょ。よっしゃよっしゃ。
ん。あれ? この Mozilla 1.4 の Address Book Palm Sync ってなんだ?
印刷が… 7.02 の方がいいかも。以前触れた Phoenix の印刷関係の問題が Mozilla, Netscape にも波及しております。うーん。