素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: twitter: @javascripter すかいぷ:javascripter_ , javascripter あっと tsukkun.net twitterにどうぞ。

転職エントリ

これまで

新卒で入社したWeb系IT企業で1年4ヶ月ほど新規事業部門でブログサービスの開発などをしていました。チームでやってるサービスと自分個人が結びつくのもあんまり良くない気がするので会社名は書かないけど、本日が最終出社日なので振り返ってみます。

やったこと

フロントエンド周りを中心にやりました。

JavaScript、Node関連ではrollup, babel, prettier, eslintなどを導入したり、すでにあったコードのリファクタリングを行ってES6 Modulesを導入したりしました。

その他、色々なバイナリを入れたりコンパイルをして動かす必要のある部分などもdockerを使って一から環境構築することなくビルドできるようにしたり、CSSはSassを使っていたものをPostCSSに変更したりしました。デプロイ周りのフローもAWSに繋いでる部分を自動にしたり色々気になったところの改善をしました。

サービスの新機能や社内ツールや未リリースのもの含め、いろいろとJavaScriptで作りました。その他、フロントエンドの業務と並行して、セキュリティ周りの調査・改善も行ったり、バックエンド周りも少し挑戦させてもらって、AWS各種サービスとPythonで一つ機能を作ったりもしました。あとReact+Reduxも覚えました。

所感

ルフレックス、リモート可、7.5時間労働で残業もなく、おしゃれで今風なオフィス、アットホームな雰囲気、どこを取ってもいい会社でした。マッサージを業務時間内に1000円で受けられたり、入社後もどんどん新しい制度ができていて、働き方改革の先端を走るような会社でした。できればずっといたいくらい。

なぜ転職するかというと25歳になって20代も残り半分になったので、若いうちにしかできないことをどんどんしていこうと思ったからです。

具体的には、これからしばらくは状況が許す限り安定より挑戦を取っていこうと思います。

これから:

次はスタートアップでサービス・アプリ開発をします。id:uiureoに誘われて開発を手伝っていた会社です。一緒に一発当ててやるぞ!

欲しいものリストは特にありません。これからもよろしくお願いします。ご飯のお誘いなども@javascripterでお待ちしております。

ユーザ操作のいるXSSで簡単にクリックを誘発させる手法と、XSS Auditorのバイパス方法

特に技術的に新しいものではないので公開せずに放置していたが、XSS Auditorをバイパスする方法はいろいろあるので気をつけましょうという話。 XSS Auditorをバイパスする方法は、Chrome XSS Auditor - SVG Bypass - Hack 2 Learnを参考にした(最新版ではすでにこの手法は塞がれた)。

実演と解説

見た方がわかりやすいと思うので、動画にしたので見てほしい。

これはある大学のログイン画面に存在するXSSで、

login.php?fromURL=xxx">Please complete the captcha before proceeding.<br><svg width=242px height=65px><a><image href=https://i.imgur.com/6W1W2jE.png/><animate attributeName=href values=javas&#99ript:alert('hi-this-is-fake')>

こんな感じのURLを入れるとfromURLに入れた値がそのままHTMLに表示されてしまっているせいで

<input id=“resumePath” name="resumePath” type=“hidden” value=“xxx">Please complete the captcha before proceeding.<br><svg width=242px height=65px><a><image href=https://i.imgur.com/6W1W2jE.png/><animate attributeName=href values=javas&#99ript:alert('hi-this-is-fake')>

このように出力されてしまう。単純なXSSであるが、セッション管理に問題があるため、fromURLがサーバーに保存されてしまっていて、一度不正なパラメータを付加したURLにアクセスするとcookieが存在している限り次回からはlogin.phpに直接アクセスしてもXSSが発生している点でSemi Permanent XSSである。

ブラウザによって実装に差はあるが、Google Chromeに限った話をするとXSSを発生させても、

?fromURL=xxx"><script>alert(1)</script>

のようなURLはXSS Auditorによってブロックされてしまい、URLをアクセスしても警告が出てしまうのでコードを実行させることは難しくなっている。 しかし、上記のようにSVGによってリンクを表示した上でクリック時にhrefをjavascriptプロトコルに変更するような特殊なアニメーションを仕込むと、XSS Auditorに検知されずにクリックによってサイトのドメイン上でJavaScriptを実行させるXSSを作り出すことができるのである。

クリックしないと発生しないXSSというのは、アクセスしただけで発生するXSSと比べると危険度が低いように思えるかもしれないが 例えば動画でデモしたように、フェイクのreCAPTCHAの認証画面のようなものを表示させると、かなり自然な形でクリックを誘導することができると思う。

まとめ

HTTPヘッダで

X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff

を設定しているサイトが増えているのはいいことだが、ブラウザのセキュリティ機能というのはあくまで二次的なものである。 XSS Auditorが高機能化しているせいで、セキュリティにある程度関心を持っていないとXSSを実行させるのはあまり手軽ではなくなっているが、XSSの脅威がなくなった訳ではない。 やる気のあるハッカーXSS Auditorを突破してくるので、サーバーアプリケーション側にXSS脆弱性が発生しないよう気をつけることが第一である。

大学を辞めたけど何も起こらなかった

あけましておめでとうございます

久しぶりのブログで新年一発目からこんなタイトルの記事を書くのもどうかと思うけど、今年からまたブログ更新していこうと思う。

経緯

去年、23歳で、あと4単位を残して大学を中退した。自分はオーストラリアの大学で短大卒に当たるAssociate Degree of Information Technologyを取得した後に3年次でBachelor of Computer Science in Science、つまり情報科学の学部に編入したのだけど、IT学部時代からずっとモチベーション維持に苦労していて、ストレスで体調を崩したりしていた。中退しないほうがいい、絶対に卒業したほうがいいと言われ続けたが、勇気を出して辞めた。

自分は中学からプログラミングをやっていてある程度プログラミングを知ってたのだけど、やりたいことも定まっていた一方(Webやセキュリティなど)、大学は広く浅くといった形のカリキュラム構成になっていたので、膨大な量の課題を流れ作業のようにこなす必要があったので、実際に興味のある分野を深く掘り下げて学ぶというスタイルで勉強することが難しく、また、趣味のプログラミングや学部と関係のない勉強をする時間が十分に取れないことに大きな不満があった。

ITを専攻していた頃は、入学前からネットワーク系以外は大体知っていたので授業中は全く別のことを勉強するかプログラミングのバイトをしていて、試験前日に大学のポータルからスライドとテキストをダウンロードして読んで、テストだけ受けてたら卒業できたので何をやったのかあまり覚えていない。

不満は書ききれないほどたくさんあったが、あとは忘れた。

案外、大丈夫だった

なんでこんなことをわざわざブログ記事にしているのかというと、大学にいた頃に、中退してしまったら大変なことになる、就職で不利になる、苦労する、人生を棒に振る、学歴差別され続ける、という脅しのような強制力を持った助言を受けたりすることが多く、またそういう不安を煽る情報が溢れていて、様々な葛藤やストレスを抱えながらもアクションを起こす勇気がずっと持てなかったからだ。

学校が合わなくて中退するか悩んでいる大学生が、辞めても結果的に何事もなく普通に働いている場面を想像するのは難しいと思う。 でも、中退したからといって全部がダメになるわけじゃなく辞めた後も人生は普通に続いていて、現に自分は希望の職種でストレスもあまりない職場でエンジニアとして普通に働いていて、生活もそれなりに充実している。

以前の自分と同じように、大学教育に様々な疑問を持っていて苦悩を抱えながらも、辞めたらどうなってしまうのか分からないので限界まで我慢している人は多数いると思う。もちろん卒業できるなら卒業したらいいし、自分が今どうにかなっているからといって、同じようにやってどうにかなる保証はない。でも、人生において保証などはそもそも存在しないし、自分が得意とすることを活かせる場所は探せばあるし、無理して苦手な場所で頑張り続ける以外にも選択肢があるということは知って損はないと思う。

辞めた後

とはいっても、周りと外れたことをすることに不安を持つのは当たり前だし、辞めた後どうなったの?ということを書いていきたい。

自分は在学中に早めに就職活動をして、いくつか内定をもらっていた。レジュメは自分でオリジナルのフォーマットでWordで作成して、ブログのURLを載せたりアルバイトの経験を書く欄を作ったりして、封筒を開けずに見えるようにクリアファイルに入れて就活フォーラムで渡したりした。その後、休学をしたりして卒業予定が狂ったので、全て内定を辞退した。なんだかんだ1年くらい経ってから、大学を中退した。 大学を中退してしばらく悩んだあと、以前内定を辞退した会社に、もう一度面接を受けさせてくださいとメールを出した。翌日に面接を受けさせてもらい、中途半端なタイミングで就職した。メールを出してから内定まで2日。自分のブログやバイトのことを載せていたので、技術的な面では大丈夫そうだということで、技術的なことは深くは聞かれず辞めた経緯などを話したくらいである。イレギュラーなことを受け入れてくれた会社には感謝している。もし落ちていたら、知り合いのツテをたどって働こうと思っていた。

ブログを書こう

人と違ったことをする上で大切なのは、自分を知ってもらうことだと思う。一般的なルートで普通の生き方をしてこなかった人を見た時、相手が最も不安に思うのは「この人は大丈夫なんだろうか?」ということに尽きる。 ブログを長年書き続けて情報発信をしながらエンジニアのグループチャットで発言をしたりtwitterなどのオープンなSNSで活動していたことは、そういった点で、自分を知らない人に短時間で自分を知ってもらう上で非常に役立った。

学校やバイト先などの環境は所属している間は居場所として機能するが、そこにしか居場所がないと、辞めてしまった瞬間に居場所がなくなってしまう。人間にとって居場所は不可欠で、居場所がないと感じると強い不安が生じるし、やる気も削がれてしまう。 特に、イレギュラーな生き方をする上では複数の居場所を持ち続ける、というのはリスク回避の意味で非常に重要だ。

インターネットは広い。自分の得意なことがニッチでも、ただ自分の考えや気持ちを書くにしても、長く書き続けていれば似たような興味や傾向を持った人が自然と集まってきて、自分を評価してくれたりする。Facebookなどの閉じたSNSの時代に、ブログを書くということの意味は、自分を発信を通じて同じような傾向の人で集まったり、自分を評価してくれる人と繋がれることにあると思う。

知ってもらわなければ自分の強みや経験、考えを生かす場所に出会えないことが多い。中2の頃にブログを始めて、プログラミングを覚えてから色々な出来事を経験してきたけれど、またブログを書こうと思うのは、ブログのパワーを感じる瞬間が今までたくさんあったからだ。

2018年の意気込み

2017年は、悩んだり、大学をやめたり、就職活動をしたり、環境の変化になれたりといったことでめまぐるしい一年だった。ブログも書こう書こうと思いながら、結局ほとんど更新できなかった。 やっと落ち着いてきたので、今年はブログもまた更新していけたらなと思う。

(たまたま!)就職先では現在、ブログサービスの開発をしていて、フロントエンド(ES6, node.js, HTML, CSSなど)をメインに、社内ツールの作成やあとバックエンド周り(AWS系, Python, SQLなど)の開発も一部やっている。業務で手に入れた知識などは当然書くわけにはいかないけれど、発信できる情報はどんどん書いていきたい。 こんなこと書いてもしょうがない、みたいなことを考えてなかなか記事が書けなかったりしたので、どうでもいいことも書いていくつもり。

以上

英文法の話パート3、which,thatの使い方についてなど

パート1: 英文法の話、時勢と動詞のニュアンスについてなど - 素人がプログラミングを勉強していたブログ

パート2: 英文法の話パート2、to doとsomething to doの見分け方と使い方についてなど - 素人がプログラミングを勉強していたブログ

質問者
The rumor that he got married is false.
The rumor that he heard is false.
この違い

javascripter
The rumor of him having got married
The rumor which he heard 
まあこれは明らかに違うな

質問者
前者はThe rumor = got marriedで後者はthe rumor≠he heeard
後者のthe rumorはheardの目的語でrumorを修飾しているから形容詞節で前者は=で結びつける接続詞で同格を表す名詞節
この辺りの見分け方がむずいけどなんとなく分かってきた

質問者
つまり最初に言ったI had a dream that I climbed Mt.Fuji.もa dream=I climbed Mt.Fujiが成立するから名詞の同格になり名詞節でthatはただの接続詞。This is the house where I was born.もthe house ≠ I was born.なので同格ではなく形容詞節か。

質問者
Climbing Mt.Fuji is a dreamでSVCが成り立つってことか。

質問者
つまりClimbing Mt.Fujiとa dreamの関係は=。being bornとthe houseは=で結べない。being born is the houseにはならないつまりSVCの関係ではないってことか

javascripter
The rumor is false. He got married. 
Then rumor is false. He heard 

javascripter
後者は抜き出すと単語が抜けてて文章が未完成になる
これが見分け方

質問者
hearの目的語がないとこが見分けるポイント?

javascripter
そう

質問者
get marriedは自動詞だから独り立ちできる文でSVで完全自動しだけどHe heardはSVOをとるものだからSVにはならないってことか

javascripter
Whichと同じで
The rumor which he heard is falseは
He heard the rumor. The rumor is false. 
みたいに
二文にわかれてるから
このタイプは、探すと抜けてる部分があるから
分離して完成しないのが後者

質問者
そっちのほうが分かりやすいかも

英文法の話パート2、to doとsomething to doの見分け方と使い方についてなど

パート1: 英文法の話、時勢と動詞のニュアンスについてなど - 素人がプログラミングを勉強していたブログ

パート3: 英文法の話パート3、which,thatの使い方についてなど - 素人がプログラミングを勉強していたブログ

質問者
I should have taken some medicine to get over a cold.
これってto get over a coldが直前のmedicine(名詞)を修飾する形容詞句とも考えられるしtake(動詞)を修飾する副詞句とも考える事ができると思うんだけど、だとしたら文の意味って2つに分かれない?
形容詞句でとらえると(私は風邪を克服するための薬を飲むべきだった)
副詞句でとらえると(私は風邪を克服するために薬をのむべきだった)

javascripter
厳密にはそうだが
Medicine, to get over a cold の方の解釈が自然

javascripter
なぜかというと
I should have take some medicine for a cold 
みたいな言い方があるから
I should have が来た時点で、~to doで
To doのためにI should haveをするべきだったっていう
Because的なものが来ることを期待してるから
Toが


javascripter
I should have got a drink to hydrate myself earlier

In order to hydrate myself earlier, I should have taken a drink
俺にはこう分解して聞こえるのが自然な感じがする

javascripter
特に
To doのあとにmyselfとかがくるも
A drink to warm myself up 
みたいになるから
MyselfはIにかかってるから
I should have got a drink, to warm myself up、のほうがmyselfが主語からきてることが明らかだから
そっちにわけて読みたくなる、
結果的には意味は同じだから
目的がどこにかかってるのかによるねこれは

javascripter
I wanted some coffee to wake me up 
I wanted some coffee to wake up

wake me upしてくれるコーヒーが欲しかったのが前者

質問者
副詞句って文全体を修飾する場合は目的が文全体にかかるのかね

javascripter
Coffee が欲しくて、その理由がto wake upなのが後者
In order toだと
目的って意味が強くなる、修飾の意味が減る

さらに、明確にするときは
Toではなくforとかwhich, whichとかthatとかで修飾

あるいは
動名詞を先にもってきて形容詞にして


質問者
まあそれもありだけど
それだとmedicineを修飾することにならない?

javascripter
I wanted some strong coffee for staying awake 

さっきいったのはmedicineを修飾する
文全体の場合は
Toを最初に持ってきて文法的にはなすか、カンマおくか、in order toとかbecause とかをいれる

質問者
まあそうなるな

質問者
because(副詞節)か前置詞+名詞(副詞句)を入れると文全体、動詞、形容詞が修飾される

javascripter
I need some coffee. I need to wake up  

I need some coffee to wake up. 
I need some coffee as I need to wake up. 
I need some coffee for waking up. 
I need some coffee in order to wake up

javascripter
I need coffee as it wakes me up 
I need coffee that wakes me up 

質問者
I need some coffee for waking upってS V O for O?

javascripter
For doing = To do

質問者
あぁ

javascripter
てかどちらでも意味が変わらないからtoで使うんだと思う

質問者
あー現在形だと状態も動作も過去現在未来の習慣だからか

javascripter
いや、そこらへんはあんまり関係ないかな
何がどこまで修飾するのかってのは英語でも 文脈によることが多くて

質問者
I drink some coffee to wake up とかは

javascripter
To wake upはI drinkの目的になってる

javascripter
some coffee to wake upで切り出すと
wake up 誰っていう部分がぬけるから

javascripter
I wake upしたいわけだから to wake up はIのほうにつく


javascripter
Grab some coffee to wake yourself up!
こういう風な言い方の場合

javascripter
コーヒーを飲んで目を覚ませ! to wake yourself upは(You should ) grabにかかってるからto wake yourself upがcoffeeにかかる

質問者
Iにかかるとしたら名詞相当語句ってこと?

質問者
つまり不定詞の名詞的用法ってこと?

javascripter
用語はわからないけども

主語を常に意識して
wake upなんだとしたら 何がなにをwake upしてるのか
そう考えるとどこできれるのかだいたいわからないかな

javascripter
I drink coffee to wake up

coffee to wake upだけだと主語が存在しないから成立しないっしょ
Coffee to wake you upみたいなかたちにしないといけない

javascripter
I drink coffee, to wake up.の場合は
I drink coffee, in order for me to wake up => I drink coffee, because I want to I wake up 

主語が連続してIだからwake upを他動にかえなくてよくなる

質問者
to wake upがdrinkにかかってるんでしょ

javascripter
I drink, to wake up
短くするとこう

dishes to be cleaned 
dishes (that needs )to be cleaned

javascripter
some coffee (that makes SOMEONE to wake up)
このSOMEONEの不在さが
coffee to wake upで区切るときの不自然さ

質問者
I drink cofee to wake up も修飾されるものはcofeeとdrink

javascripter
コーヒー自体は立ち上がらないわけだから

javascripter
背後に主語があるはずなのに、名刺単位でそこで区切られると
主語の情報がきれる

javascripter
いや、to wake upはI drink coffee全体にかかってる

質問者
私は起きるためにコーヒーを飲む
私は起きるためのコーヒーを飲む
私はコーヒーを飲む起きるために

javascripter
becauseとかと同じで補足情報だから。

質問者
起きるために私はコーヒーを飲む

javascripter
I wake up to drink coffee

質問者
それだと名詞的用法になる

質問者
to drink cofeeは目的語

javascripter
I drink coffee to wake up
I drink coffee to clear my mind
I drink coffee to help wake up early

javascripter
ちょっと質問の内容見失いかけた
なにが聞きたいんだっけ

質問者
いや、形容詞句ともとらえられるし副詞句ともとらえられるからそれだと全く違う意味になるんじゃないかって思って

javascripter
全く違う意味になることはあるよ
ただ
I drink coffee to wake upの場合は
I drink something, and that something is coffee to wake up
に分解するとcoffee to wake up,になって 主語が消えるから分解できるパターンは
(I drink )coffee (to wake up)

しかありえないんじゃないのっていう

質問者
つまり名詞を修飾するってこと?

javascripter
toがSVの目的を示してる

javascripter
名刺を修飾していないとおもうよ

javascripter
In order to get ready for work, I drink coffee in the morning to wake (myself) up.

javascripter
これくらい長くなるとわかるとおもうけど
toが名詞を修飾しているというのはワンパターンにすぎなくて
分全体を修飾していることが多い

質問者
She became beautiful to enter a contest.

質問者
うーん

javascripter
She became beautiful because she wanted to enter a contest.

javascripter
これもto doが名詞を修飾していないよ

質問者
S V C to doはCとVと文全体かな

javascripter
to doは分全体とは関係ないから分けて考えたほうがいい

javascripter
なんか長めの文章ないの

質問者
なんかだして

質問者
He has gone to buy some cigarettes.

javascripter
I woke up at 7am to go to the gym before classes to make this a habit for my future self to be healthy

javascripter
I woke upが基本で
 To go to the gymがその理由
to make it(going to the gym) a habit. がI woke up to...からto go to the gymにかかってて
for my future self to be healthyは、to make this a habitにかかってる

ほとんどがbecauseで置き換えられるんだって
I woke up at 7am to go to the gym before classes, because I want to make a habit of going to the gym, because I want my future self to be healthy. 

質問者
(主節)I(代名詞・主語)woke up(自動詞)at7am(副詞句)to go to the gym(副詞句で動詞を修飾している)(従属節)because I(主語・代名詞)want(動詞) to make a habit of  going to the gym(不定詞の副詞的用法で動詞を修飾している),(従属節)I(主語・代名詞)want (動詞)(目的語)my future self (不定詞の形容詞的用法で直前の名詞selfを修飾している)to be healthy.

javascripter
I want to have some medicine for my sleeping issues
I want to have some medicine to sleep properly 
I want to have some sleeping pills
I want to have some medicine to fall myself into sleep faster


javascripter
どこが 薬局の人への説明で
どこが薬の説明なのか 意識して日本語に訳してみ

これは文脈からしてどれも全く意味がかわらないので
存在しないんだよな明確な訳が

javascripter
つまり 特定の場合を除いて toを使った結果、目的がどの部分にかかっているのかはwhichと同じで
どこに対してでもありうることがあるということで

javascripter
文脈からありえない可能性を排除していくしかない、厳密に書きたい場合はToですまさずにFor someone toとかin order for someone toとか, whichやthat、あるいは二文にするなどしてわけないといけない

質問者
for my sleeping issuesって形容詞句と副詞句で分かれるんじゃね。
形容詞句でみるとmedicineを修飾するから(私は不眠の薬を飲みたい)になって副詞句でみると(不眠のため私は薬を飲みたい)よく考えたらin orderも形容詞句の意味を持つから明確な訳がない。明確にするとしたら節で繋ぐのがいいのかね。 おやすみ。

javascripter
I want to have some medicine for my sleeping issues 

javascripter
だと
Myがあるから
目的はI want to haveにかかってると捉えるのが自然

javascripter
なぜかというとmedicine for my ...みたいに自分に限定されたmedicineが欲しいわけではないから

javascripter
逆にI want to have some medicine for sleeping issuesだと
medicine for sleeping issues
とかかっていると捉えるのが自然

javascripter
同様にto sleep properlyもI want to have some medicine全体にかかってる
Some sleeping pillsはまあそのままで
To fall myself...も文全体にかかってる


質問者
i should have taken 〜
薬を飲むべきだった(飲まなかったので後悔している)
風邪を治すための薬を飲むべきだった(薬は飲んだけれども違う薬を飲んで後悔していると考えられる)
ってなんのかな

javascripter
これは曖昧性があるな、どちらともとれるな。この場合は、文章からは区別できない
発音時に
I should had taken some medicine FOR A COLD, not for headaches 
みたいに
後者の場合にforを強調して発音すると後者に聞こえる
普通は前者

ただし、文章からは区別できない

質問者
ほう

英文法の話、時勢と動詞のニュアンスについてなど

忙しくてブログを全然更新していなかったが、最近、友達に英文法を教えているので、 役に立つかもしれないのでログを載せておく。

パート2: 英文法の話パート2、to doとsomething to doの見分け方と使い方についてなど - 素人がプログラミングを勉強していたブログ

パート3: 英文法の話パート3、which,thatの使い方についてなど - 素人がプログラミングを勉強していたブログ

質問者
Leaves turned red in fall.とLeaves got red in fall.って同じ?

javascripter
同じ。Turnedのほうがただ、
変わったという動作が少し強調されてる感じするがほぼ変わらないと思う

質問者
なるほどね

javascripter
I got 20 years old
I turned 20 years old

javascripter
これだとturnedのほうが
誕生日迎えたっぽさが強い感じするっしょ

質問者
ネイティブはどっちも使うん?

javascripter
使う
Turnのほうがでもいい

質問者
ネイティブらしく使うには洋書とか読んで覚える?
背景にあるロジックとかニュアンス的な部分

javascripter
そもそも
TOEIC600くらいはないと
読めないよたぶん

javascripter
ある程度点数あがってからはじめて色々まあまあ読めるようになって細かいニュアンスの違いがわかってくる

質問者
なるほどね

質問者
Leaves turned red in fall. it suddenly got dark. Leaves got red in fall. It suddenly turned dark. 同じ意味?

javascripter
二個目のleaves got red in fallが
Leaves turned red in fallより動作の強調具合が弱い以外同じ

javascripter
Suddenly got 
Suddenly turned
これ入ると同じ

javascripter
百円もらった
百円受け取った

意味は同じでも
受け取ったみたいに、意味が限定されてる言葉のほうがちょっとフォーマル寄りで意味が強い感じがするでしょ
そういう感じ

質問者
これ動詞の選択問題でどちらもsvcをとりどちらも〜なるって意味だけどどれが適切なのか分からなかったから

質問者
答えではturned red got darkだった

質問者
その程度の違いなのか

javascripter
その程度の違いだけど
Turnedにはもともとsuddenlyのニュアンスが含まれてるから

javascripter
Gotにsuddenlyをつけるほうが意味がダブらない

ただ急にという意味を強く込めてsuddenly turnedという言い方も普通にするから
まあ、どういう話なのかによるけどね

質問者
Everyone likes to hear the chimes.って文あってる?

質問者
みんなはそのチャイムの音を聞くのが好きですって訳されてるけど

javascripter
あってる
I'd like to
I would like to
これはわかるっしの
これが現在になっただけ

質問者
好きかどうかはチャイムを聞いてみないと分からんくね

javascripter
聞いてるから好きなんだろ
過去から未来全体だから現在で、
I like to do it 
I like doing it
どちらもありうる

javascripter
To hear the chimes is what everyone likes 
こういうことだよ

質問者
チャイムを聞くことが未来に向いてるけど、それまでに聞いたことがあって好きだと分かってるなら動名詞じゃないの

javascripter
I avoid taking to people. 
人と話すことを避けます
I try to avoid talking to people. 
人と話すことを避けようとしてます

javascripter
未来に向かっている動作が含まれている場合は
動名詞とは限らない
To doも普通にある

質問者
Everyone likes to hear the chimes.ってみんなはチャイムを聞くことが好きって話し手は言ってるけどto doは未来を表していてまだ聞く行為を行っていないのになんで好きって分かるの?って先生に聞いたらそれは間違ってるって言われた

javascripter
People vary in how they apply these phrases, but I reckon the general pattern is:

"I would like to do" is for hypothetical situations.

"I like doing" is for describing things that you actually do.

There's a slight difference in meaning between "I like to exercise" (I think it's the right thing to do) and "I like exercising" (I enjoy it).

That's how I see the difference, but I know some people have other ideas.

ネイティブの意見は
Like to doもlike doing もどちらも正しい

質問者
おっけ

javascripter
えっと
さっきから言ってるけどto doが未来を表してるから未完了だからダメっっていうのが違くて
好きっていうのは
そもそも
未完了で未来ってわけではなく
現在系だから

javascripter
To do「すること」「が好き」

っていう風に分解されてる

質問者
聞くことが好きって既に聞いたことあるから好きじゃないの

javascripter
すでに聞いたことがあり、そして
好きという状態が、過去、現在、未来にわたっているから

javascripter
全体でみて
Likeが現在である限り
Doingでもto doでも関係がない
そもそも時制をきめてるのはto doかdoingかではなく
Likeの部分だから

質問者
likeは状態動詞だね

javascripter
うん

質問者
like doing = 一般的、習慣的なこと、進行中のこと、体験済みのことなどに、一方
like to do= その場の特定の行為や未体験、未来の行為に用いると説明されて   います。

like doingはチャイムを聞いて体験済みで好き状態になってるんじゃないの

javascripter
うん

javascripter
いや

質問者
話し手はなんでチャイム未体験なのに好きだと判断したの

javascripter
そもそも
Everyone がto hear the chimes を体験してること自体が不自然なわけで

そもそも全員が聞いているということ自体が、強調というか仮定なわけで

javascripter
Everyone likes to hear the chimes because the chimes are so nice 

javascripter
こういう可能性もあるわけで

質問者
あー 話し手の仮定ってこと?

javascripter
仮定ってほど仮定ではなく事実のこともあるけど

質問者
でもそれだったら仮定文になるんじゃないの

javascripter
仮定ではなく強調ってことだよ

javascripter
俺は絶対に勝つ

javascripter
これは未来だけど
現在になってて強調されてるでしょ

質問者
まあ

質問者
everyone likes to hear the chimes(みんながそのチャイムを聞けば好きな状態になると確信している)ってこと?

javascripter
喋り方にもよるけど

javascripter
ぶっちゃlike doingと変わらないけど
まあ原理的にはそうといえばそう
はむずいけどこれ以上説明しようがない

質問者
あーたしかにlikeの説明で理解した

質問者
I go to the gym to do exercise. じゃあこれも現在形で過去現在未来において変わらぬ真理だから文としてはあってる?

javascripter
I go to the gym (in order )to do the exercise
この場合は目的だから、For doing the exerciseでもよくて

質問者
これだと、習慣的な動作どうしだからto doのところはdoingでもいいのか

javascripter
どちらでもいい
目的を表してる補足説明だから時制はI goの部分で表現されてる

質問者
現在形の動作動詞自体に習慣的な動作の意味が含まれてるから

javascripter
そう
I go to work everyday for my future

質問者
likeも過去現在未来においての状態だからそう考えるとto hearも理解できるな

javascripter
あくまで、時制は動詞部分にあるから

それ以外の部分は明確には時制は示されていない
To doが未来っぽいのもあくまで傾向なだけで
基本の時制は全て動詞にある
For my futureみたいに、途中で別の未来を表したいなら、そこで書く

To doが未来なのは
I go to the gymみたいに、
Toがもともと目的地みたいな、先のものを示すからで
逆にdoing は現在している動作だから
すでに行われている=過去にはじめて現在の動作
というふうに
そこからニュアンスの違いが生じてる

質問者
なるほど

javascripter
まあそんな感じ

質問者
To自体に目的の要素が含まれてることは知ってたけどto doの方ばかりに目がいってたわ

javascripter
言葉ってのはそもそも
辿っていくとシンプルなもとになってる発想と使い方があって
いろんなことを表現するために、そこから派生してるから

javascripter
よーく考えると背後にはまあまあロジックがある

一つのファイルでサーバーとHTMLファイルとコードを共有するやつ

ちょっと説明がし難いがindex.htmlというファイルがあったとして、ファイルプロトコル経由で

file://path/index.html

などのように直接ブラウザで起動すると、プロトコルの関係でファイルが読み込めなかったり、エラーになったりすることがよくある。サーバーをたててlocalhostなどから読み込めばいいのだが、微妙にセットアップが面倒臭い。下記コードをindex.htmlとして保存すると

node index.html

とするだけでブラウザが立ち上がり、index.html自身をルートとしてそのディレクトリ以下のファイルを配信する簡易サーバーが立ち上がる。具体的には、

http://localhost:port/index.html

のような形でアクセスできるようになる。コマンドを実行したディレクトリの絶対パスをもとにポートを自動生成しているので、すでにサーバーが起動済みの場合、

node index.html

は新規にサーバーを立ち上げずに、既存のlocalhost:port/index.htmlをブラウザで開く。 特殊な魔法を使っているので、HTMLとしてもJSとしてもvalidで、ブラウザでもnodeでもコードの一部を共有している。

<!--
(function () { /* -->

<!DOCTYPE html>
<html>
  <head>
    <title>Title</title>
    <meta charset="UTF-8">
  </head>
  <body>
    <p>Hello!</p>
  </body>
</html>

<script src="index.html"></script>
<!-- */

// Shared among server and client sides
function mapPort(s) {
  var hashCode = 0;
  for (var i = 0; i < s.length; i++) {
    hashCode += hashCode * 31 + s.charCodeAt(i);
  }
  return 49152 + hashCode % 16384;
}

// Only for browser

if (typeof window !== 'undefined') {

  if (location.protocol == 'file:') {
    var url = 'http://localhost:' + mapPort(location.pathname);
    var req = new XMLHttpRequest();
    req.open('GET', url, false);
    try {
      req.send(null);
      location.href = url;
    } catch (ex) {
      document.writeln("<p>Server is not running. Please run the server by node index.html</p>");
    }
  }
  return;
}

// Only for Node.js

// Running a simple server.

var http = require('http'),
  fs = require('fs'),
  child_process = require('child_process'),
  path = require('path');

var ROOT_DIR = path.dirname(process.argv[1]);

function openInBrowser(url) {
  return child_process.spawnSync('open', [url]);
}

var port = mapPort(process.argv[1]);

var server = http.createServer(function (req, res) {
  var fPath = path.join(ROOT_DIR, req.url,
    /\/$/.test(req.url) ? 'index.html' : ''),
    file = null;
  try {
    file = fs.readFileSync(fPath);
  } catch (e) {
    // NOOP
  }
  res.writeHead(file ? 200 : 404);
  res.end(file || "File Not Found");
}).listen({port: port }, function () {
  console.log('successfully established');
  openInBrowser('http://localhost:' + port);
}).on('error', function () {
  console.log('already open?');
  openInBrowser('http://localhost:' + port);
});


})(); // -->

DBにおけるウェブシステムのアカウントの権限管理とセキュリティについて

下記にセキュリティに関する追記あり。読了後、そちらもチェックしてください

ユーザーと管理者アカウントがあるウェブサイトで、管理者がユーザーのパスワードをリセットできたりといった風に、アカウントによって権限が違う場合のセキュリティについての考察である。

/loginにアクセスし, emailとパスワードでuser/adminにアクセスできるとする。 (ログインページは1つであり、user/admin間のemailは重複してはいけない)

論理的構造を考えると account->user, account->admin といった階層構造になっているので、自然に設計すると accountテーブルにuser/admin情報をもたせて、

pragma foreign_keys=1;

CREATE TABLE account(
  id INTEGER PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  password TEXT NOT NULL,
  type INT NOT NULL,
  FOREIGN KEY(type) REFERENCES type(type)
);

CREATE TABLE type(
  type INTEGER NOT NULL PRIMARY KEY,
  type_name TEXT UNIQUE NOT NULL
);

INSERT INTO type VALUES
  (0, 'user'),
  (1, 'admin');

INSERT INTO account VALUES
  (0, 'user@example.com', 'pass1', 0),
  (1, 'admin@example.com', 'pass1', 1);

と書きたくなる(簡略のため生パスワードを使っているが、もちろん真似してはいけない)。これは、セキュリティの観点からは好ましくない。ログインページの実装を考えると、ログイン処理は

SELECT * FROM account WHERE email=$email AND password=$password;

になると思う。 $emailにSQL Injectionがあったとすると、$emailが

0 OR type=1 --

であった場合

SELECT * FROM account WHERE email=0 OR type=1 -- AND password=$password;

が実行され、管理者権限のアカウントが乗っ取られてしまう。 また、ログインに使用するemailアドレスを変更する機能を考えると

UPDATE account SET email=$email WHERE id=$id;

になるが、ここで、$emailにエスケープ漏れがありSQL Injectionがあるとする。$emailが

'a@example.com' type=1

であったとき、

UPDATE account SET email='a@example.com' type=1 WHERE id=$id

が実行されてしまい、ユーザーが管理者に昇格してしまう。これらは重大なセキュリティホールなので、SQL Injectionがあったとしても特権昇格ができないようなDB設計が好ましい。

具体的には、userとadminのテーブルを分けるのが好ましい。ただし、仕様上idとemailはuser/admin両方で一意であることを保証しなければならないので、完全に分けることは難しい。ここでは、id_tableを使った方法ともう一つの方法を考える(あまりSQLに詳しくないので、いい方法を知っている人がいたら教えてください)。

pragma foreign_keys=1;

CREATE TABLE id_email(
  id INTEGER NOT NULL,
  email TEXT NOT NULL UNIQUE,
  PRIMARY KEY(id, email)
);

CREATE TABLE user(
  id INTEGER NOT NULL UNIQUE PRIMARY KEY,
  email TEXT NOT NULL UNIQUE,
  password TEXT NOT NULL,
  FOREIGN KEY(id, email) REFERENCES id_email(id, email)
);

CREATE TABLE admin(
  id INTEGER NOT NULL UNIQUE PRIMARY KEY,
  email TEXT NOT NULL UNIQUE,
  password TEXT NOT NULL,
  FOREIGN KEY(id, email) REFERENCES id_email(id, email)
);

BEGIN;
INSERT OR ROLLBACK INTO id_email(0, 'user@example.com');
INSERT OR ROLLBACK INTO user(0, 'user@example.com', 'pass');
COMMIT;

emailから権限を判断する手段がないので、ログインはadmin, userの順に試行する。

上記ではidとemailのシステム内での一意性を保証するためにid_emailテーブルを定義したが、この方法もセキュリティ上よろしくない。

ユーザーがログインemailを変更するとき、userテーブルではなくuser/admin共通のid_email自体を参照しなければいけないので、

UPDATE id_email SET email=$new_email WHERE id=$id;

のようなコードを書くことになる。

この時、$new_emailでSQL Injectionが可能である場合、$new_emailが

'attacker@example.com' WHERE id=1 --

だとすると

UPDATE id_email SET email='attacker@example.com' WHERE id=1 --WHERE id=$id;

となり、管理者権限のアカウントのemailを攻撃者のものに書き換えられる可能性がある。書き換えが成功した場合、パスワードリセット機能によって管理者権限を奪われてしまう。

よって、id_emailを触るときには鬼のように気をつけるか、そもそもid_emailを使わないように定義するしかない。

id_emailを使わない方法についてだが、emailをuser/admin内で定義したい場合、テーブルにまたがった制約は外部キーや複合キーなどではうまく表現できないので、トリガーを使う(いい方法を知ってる人がいたら教えてください)。 また、システム内のidはaccountテーブルを使って一意性を保証しているが、アカウント作成時にidを取得する以外では使わないので特に問題はない。

pragma foreign_keys=1;

CREATE TABLE account(
  id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT
);

CREATE TABLE user(
  id INTEGER NOT NULL,
  email TEXT UNIQUE NOT NULL,
  password TEXT NOT NULL,
  PRIMARY KEY(id),
  FOREIGN KEY(id) REFERENCES account(id)
);

CREATE TABLE admin(
  id INTEGER NOT NULL,
  email TEXT UNIQUE NOT NULL,
  password TEXT NOT NULL,
  PRIMARY KEY(id),
  FOREIGN KEY(id) REFERENCES account(id)
);

-- user内でemailがUNIQUEであることは保証されているのでadminのみをチェックする
CREATE TRIGGER insert_user_email AFTER
  INSERT ON user
  FOR EACH ROW WHEN (SELECT email FROM admin WHERE email=new.email) IS NOT NULL
  BEGIN
    SELECT RAISE(ROLLBACK, 'email must be unique');
  END;

CREATE TRIGGER update_user_email AFTER
  UPDATE OF email ON user
  FOR EACH ROW WHEN (SELECT email FROM admin WHERE email=new.email) IS NOT NULL
  BEGIN
    SELECT RAISE(ROLLBACK, 'email must be unique');
  END;

-- 同様に、adminにおいてはuser内に同じemailが存在しないかをチェックする
CREATE TRIGGER insert_admin_email AFTER
  INSERT ON admin
  FOR EACH ROW WHEN (SELECT email FROM user WHERE email=new.email) IS NOT NULL
  BEGIN
    SELECT RAISE(ROLLBACK, 'email must be unique');
  END;

CREATE TRIGGER update_admin_email AFTER
  UPDATE OF email ON admin
  FOR EACH ROW WHEN (SELECT email FROM user WHERE email=new.email) IS NOT NULL
  BEGIN
    SELECT RAISE(ROLLBACK, 'email must be unique');
  END;

BEGIN;
INSERT OR ROLLBACK INTO account VALUES(null);
INSERT OR ROLLBACK INTO user VALUES (last_insert_rowid(), 'user1@example.com', 'pass');
COMMIT;

下記では$adminなどの変数が正しいメールアドレスであると想定して、読みやすくなるよう$emailのまま使用しているが、 外部に公開するサイトでは決してやってはならない。必ず、SQLプレースホルダを使用し、(できれば)ペネトレーションテストも行うべきである。

'attacker@example.com' WHERE id=1; UPDATE admin SET email='attacker@example.com';--

ログインするには、最初はadminとしてログインを試行して

SELECT * FROM admin where email=$email AND password=$password;

をして、失敗したらuserとして

SELECT * FROM user where email=$email AND password=$password;

で再試行する。ユーザのemailの更新は

UPDATE user SET email=$new_email WHERE id=$id;

なので、たとえSQL Injectionができても管理者権限を奪うことはできない(ただし、テーブルのアクセス権限を正しく設定している場合)。パスワードの変更などについても同様である。このように、権限の異なるアカウントは特権昇格攻撃を未然に防ぐためテーブルごと完全に分けたほうがよい。

さらに言うと、そもそも1つのログイン画面で複数の権限のアカウントの管理をすること自体、実装が複雑になるのでセキュリティ上好ましくない。

一般ユーザのログインは /login にして、 管理者のログインは /admin/login にし、idはadmin_id, user_idなど関連性がないことが明らかになるように名前をつけadmin/user間のemailの重複は無視し、userとadminを完全に分けるのが、安全な設計だと思われる。

「ORMを使ってまともに書いていたらSQL Injectionは起きないので関係ないし、親クラスに相当するものがあるほうが自然だよ」という考え方もあるが、最近「権限の異なるアカウントのテーブルは必ず分けないといけない」と断言されたので、いい機会なので考えてみた次第である。断言口調で書いたものの、絶対そうでなければならないかと言われると、うーんという感じではある。

おまけで一応書いておくが、生パスワードをDBに保存してはいけない。

hashed_password = password + salt; // saltが後ろである必要はない
for (i = 0; i < 1000; i++) {
  hashed_password = hash(hashed_password)
}

このようなニュアンスのものを使うべきである。詳しくは「パスワード ソルト」で検索してほしい。

実はあまりSQLに詳しくないので、他にidとemailの一意性を保証する上手い方法があるような気がしてならないので、いい方法があったらコメントで教えてください。

計算量とBig-O記法

プログラマであればアルゴリズムに関する話で、O(n)だとかO(log n)だとか、O(n2)だとか、そういった記号を目にすることはよくあると思う。 なんとなく、log n < n < n2の順に計算量が増加していくとかそういうことも知っていると思うが、計算量の増加とは何か説明しろと言われると、なかなか難しいと思う。この記事では高校数学レベルでわかるように考えていきたい。

まず、計算にかかる時間を、nを入力のサイズとしてf(n)を計算にかかる最大時間を返す関数とすると、計算量一般を、O(f(n))という、入力に対する計算時間の増加率として定義することができる(より厳密には、f:R+ -> R+ where R+=[0,∞)で、a>bであればf(a) >= f(b)であるとき)。

g(n) = n ^ 2

はn = 1に対して1を返す一方、より増加率の低い

h(n) = n + 100

は、n = 1に対して101を返すことに注意したい。言い換えると、関数の増加率の大きさと数字の大きさは違う。

では、どのように比較すればいいかというと、nが十分に大きくなると、増加量が大きい関数(n)>増加量が小さい関数(n)になるという性質を利用する。つまり、nを無限大に近づけて行ったときにおけるlim (n -> ∞)における大きさの違いを利用する。 ここで注意したいのは、正の極限値付近について

lim (n -> ∞) n + 100 = ∞
lim (n -> ∞) n ^ 2 = ∞

であることからわかるように、limによって導き出した∞は極限であって、大小の概念を含む一般の数値と違うことから、大小の比較には使えないということである。ただし、∞と∞の比較を避けることができれば、limを使っても、極値での大きさの比較はできる。つまり、

lim (n -> ∞) g(n) / h(n)

というように、比較したい二つの関数を分数の形にして∞自体を直接比較しなくて済むように変換する。

追記:コメント欄で指摘されたが、上記の定義だと、関数によっては振動して収束しない場合がある。limではなく、limsup/liminfを使って、上極限/下極限について考えると、これらも含めて定義できる。例えば、limsup( n -> ∞) |g(n) / h(n)| < ∞であればg(n) = O(h(n))、など。

この場合、lim(n -> ∞) g(n) / h(n)の値をkとしたとき (g(n)もh(n)も定義により常に正なので0/0は生じないことに注意)

追記2:最初O(f(n)) < O(g(n))と書いていたが、記号の使い方が不適切との指摘を受けたので訂正した。

  • kが正の無限大に発散するとき: O(f(n)) ⊊ O(g(n))
  • k∈Rでk != 0のとき: f(n) ∈ O(g(n))かつg(n) ∈ O(f(n))
  • k=0: O(g(n)) ⊊ O(h(n) となる。

先ほどの例の場合、

lim (n -> ∞) (n^2) / (n + 100) = ∞

となり、nは∞になるので、O(n2)の増加率のほうがO(n + 100)より大きいことがわかる。同様に lim (n -> ∞) (n + 100) / n = 1なのでO(n + 100)とO(n)が同一であることなどもわかる。

参考: ランダウの記号 - Wikipedia

promiseを順番に実行する

JavaScript の Promise を返す関数を直列で実行したいので Pinscher というライブラリを作ってみた。 - (define -ayalog '())について。 例えば、非同期に実行されるPromise p1, p2, p3があったとしてcallbackをp1, p2, p3の順番に実行したい場合、reduceが使える。

例1は、p1, p2, p3を並列に実行した上で、callbackの順序を保証したい場合。

まずテスト用に、一定時間まってからvalueでresolveする関数resolveLaterを定義しておく。

function resolveLater(value, ms) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(value);
    }, ms);
  });
}
var promises = [
  resolveLater('a', 1000),
  resolveLater('b', 1000),
  resolveLater('c', 2000),
  resolveLater('d', 0),
];

var callback = console.log.bind(console);

promises.reduce(function (current, next) {
  var p = current.then(function (v) {
    return next;
  });
  p.then(callback);
  return p;
}, Promise.resolve());

// a, b, c, d

このようになる。タスクそのものは並列に実行されるが、callbackは順番に実行される。

逆に、p1, p2, p3自体を順番に実行したい場合、つまり、あるタイミングで同時に動いてるpromiseが1つである必要がある場合。(ネットワークアクセスなどで、負荷をかけたくない場合などはこっちのパターンに近い実装をすることになる)

var argList = [
  ['a', 1000],
  ['b', 1000],
  ['c', 2000],
  ['d', 0]
];

var callback = console.log.bind(console);

argList.reduce(function (current, args) {
  return current.then(function () {
    return resolveLater.apply(null, args).then(function (v) { callback(v); });
  });
}, Promise.resolve());

非同期コードのテストについてだが、Jest | Painless JavaScript Unit Testingがおすすめで、jestを使うと、setTimeout系の関数自体を乗っ取って(実行順序を保証しながら)同期的に実行しながらsetTimeout.mock.callsに呼び出された時のmock関数の引数を記録していってくれるので、実際の非同期で不確実な動作に依存せずにテストしやすいコードを書ける。

ただ非同期のテストはどちらにしろ複雑になりがちで、ある程度のところで諦めてテストケースは書くがチェックは手動、という風にするのが現実的なのかもしれない。

追記

コメントでライブラリで解決していることを実現できていないと指摘されたので、その他の例について触れる。

まず、上記コードにはなかった、順番に実行したいpromiseがコード中で動的に追加される場合。 個々のタスク自体は並列に実行するが出力を順番にしたい場合

var callback = console.log.bind(console)
var task = Promise.resolve();

var identity = Object; // この場合function (a) { return a; }

task = task.then(identity.bind(null, resolveLater('a', 1000))).then(callback);

task = task.then(identity.bind(null, resolveLater('b', 1000))).then(callback);

task = task.then(identity.bind(null, resolveLater('c', 2000))).then(callback);

task = task.then(identity.bind(null, resolveLater('d', 0))).then(callback);

このようにreduceを手動で展開することで動的に追加できるようになる。

もう一つの例として、個々のタスクの実行も順番に(同時に一つしか動かさないように)したい場合

var callback = console.log.bind(console)
var task = Promise.resolve();

task = task.then(resolveLater.bind(null, 'a', 1000)).then(callback);

task = task.then(resolveLater.bind(null, 'b', 1000)).then(callback);

task = task.then(resolveLater.bind(null, 'c', 2000)).then(callback);

task = task.then(resolveLater.bind(null, 'c', 0)).then(callback);

このようになる。 Promiseの上にコントロールフローを実装するライブラリを使うより、このように直接promiseを組み合わせる手法を使ったほうがいいと思う理由は複数あって、まず、自由度の点。 例えばここに載せたコードでは、全体の状態がPromiseとして表現されていて取り出すことができる(reduceを直接使った例では、reduceが返すPromiseがそれにあたる)ので、複数のパターンを組み合わせることができる。 具体的には、最初のpromise数個をarrayとして入れておき、動的に実行したいものを後から追加したい場合。

var identity = Object; // この場合function (a) { return a; }
var callback = console.log.bind(console);

var task = [
  resolveLater('a', 1000),
  resolveLater('b', 1000),
  resolveLater('c', 2000),
  resolveLater('d', 0),
].reduce(function (current, next) {
  var p = current.then(function (v) {
    return next;
  });
  p.then(callback);
  return p;
}, Promise.resolve());

task = task.then(identity.bind(null, resolveLater('e', 1000))).then(callback);

このようにできる。

reduce相当の処理をしていないコードのもう一つの問題は、エラー処理。動的にタスクを追加する例では全体のエラーという概念は薄いかもしれないが、例えば、記事中のコントロールフローライブラリでは、すべての実行が終わった状態というものをPromiseとして取り出せない。よって、全体のどこかでエラーがあった場合にそれらをcatchする関数、というものが書けない。

全体の処理がPromiseとして表現されている場合は

task.then(null, function (err) { console.error(err); });

などを最後に追加することで、エラー処理が握りつぶされるわかりづらいエラーが防げる。