うさぎともふもふしたりうさぎともっふもっふしたり。プログラミングしたり。
2010-05-13
setIntervalとsetTimeoutを調べた結果余分なことになった
なかなかどうして、怠惰な若輩者につき不明な点が多々あるため先人の知恵をお借りしたく候。というわけでいろいろ知恵や知識などいただきます。・・・消化不良でしたorz
setTimeoutの中の小難しいお話
自分だけじゃ到底調べられないし分かる術もない。Cなんて知るか。だのにこうしてなんとなくでも知ることができるのはありがたいことです。
Firefoxだけで少し古い記事だけど今でも同じようなものかな。他に私がわかるような資料もないのでこちらをベースに考えさせてもらいます。大きな変更はないだろうし。
タイマースレッド
URL先読んだだけではイメージできなかった。整理のため箇条で要約。
- Firefoxにはタイマー処理だけを管理している専用のスレッドがある(TimerThread)
- タイマーの時間切れ処理を行っていて、やることなかったら寝てるだけ
- 100msごとに起きて確認し、やることなかったらやっぱり寝る
- setTimeoutが呼び出されると、なんやかんや処理してタイマースレッドが管理しているタイマーリストに○○ms後に起こしてね(はぁーと)とリクエストを登録
- タイマーリストに登録しただけでは寝過ごすので、その内部の関数使って呼んで起こしている
setTimeoutを呼び出した側のスレッドが呼び出されたときにやっていることの流れはこうなっているらしい。
タイマースレッドの主なお仕事は2つ。
1.タイマーリストのチェック
- 普段は眠っていてsetTimeoutからPR_NotifyCondVar()という内部の関数が呼ばれると目覚める
- その後タイマーリストをチェックして、一番早くタイムアウトするタイマーの時間を調べる
- 今から○○msあとにタイマーを呼び出す必要があることを調べたら、waitForとかいうとこに入れてそれまで寝る
2.タイムアウトイベント通知
- ○○ms経過すると目覚めお仕事に取り掛かる
- ○○ms経過する間にタイムアウトしたタイマーを調べ、そのタイムアウトイベントをタイマーを設定したスレッドのイベントキューに追加
タイマースレッドのお仕事は、時間切れになったタイマーをチェック。そしてタイムアウトイベントをタイマーを設定したスレッドのイベントキューに追加するということでしょうか。それをやったらタイマースレッドはまた寝るようです。
それからキューに追加するのはそのsetTimeoutで設定された関数を実行するためのnsIRunnableというオブジェクトを追加するだけで、実行はしない。実行はJavaScript側ということでしょう。
ということで全部を自分なりに流れをまとめると、setTimeoutを呼び出す部分でタイマースレッドさんは目覚める。ついでタイマーリストに時間とそのsetTimuoutで使う関数が登録される。タイマースレッドさんは一番早くタイムアウトするタイマーの時間を調べてそのときに起きれるよう、目覚ましみたいなもんをかけてまた寝る。
タイマースレッドさんは目覚ましが鳴ると、タイムアウトしたタイマーのタイムアウトイベントを設定されていたスレッドのイベントキューに追加して、仕事を果たし満足して寝る。そしてその後実行はJavaScript側が行う、という流れで合っているかな。
まあ穴はあるだろうがなんとなくイメージはつかめたんじゃないかと思う。
イベントキュー
これはJavaScript側のスレッドのキューですよね。ここがその、シングルスレッドがどーのこーのいわれている部分なのかな?HTMLの再描写やマウスなどイベント関連は全てここに登録されひとつずつ実行されているみたい。それでここが時間が遅延したりする原因部分だと。
コードを借りると、
setTimeout( function () { timeout_handler() } , 100 ); very_heavy_Computation();
ヘヴィな関数が実行されていると、その処理が遅れた分だけ遅れるんでしょう。だから必ずしも100ms後にtimeout_handlerが呼び出されるわけではないらしい。
それからここの部分は初心者にありがちな勘違いで。私も勘違いしていた。setTimeout系よりも先にグローバルなコードが実行されるので、
setTimeout(function() { alert(1) }, 100); alert(2); // 2 // 1
グローバルコードがまず最初にイベントキューに追加され、それからsetTimeout系が順次追加されていくって流れか。ここを理解せず安易に複数のタイマーを使うと非常に悲しいことが起きる。というか起きた。
これでJavaScript側のスレッドと、タイマーを管理する専用のスレッドがあるってこと、そしてその2つの関係がなんとなくイメージできたと思い込もう。
setIntervalとsetTimeoutの違い
どういう場合にどちらを使えば良いのか、という基準が分からない。setTimeoutでもまわせるし、わざわざそうしているコードもある。
違いについてはよくわからないので検索。
setTimeout() → 処理が終わってから次の処理を始める
setInterval() → 処理が終わっていなくても指定の時間毎に処理を始める
http://blog.wonder-boys.net/?p=226
そうだったのか。って実はこういうことは知っていた。ただなんか本当なのかなと疑問に思うのだけど。
setTimeoutはスタックやランタイムを考慮します。
setInterval はそんなことには無関係に杓子定規に指定時間間隔で
処理を機械的にします。
なので、重い処理だと、完了していないのに、時間切れで次の処理
を実行して大変な修羅場になります。
http://questionbox.jp.msn.com/qa3098199.html
やっぱりそういうことでいいのかな。
もしあなたがユーザの操作をが必要ないなら提供されたコードを繰り返し実行する setInterval を使うのが一番です。
https://developer.mozilla.org/ja/Canvas_tutorial/Basic_animations
こういう使い分けで良いのか。
大量のsetIntervalやsetTimeoutを呼び出す場合
かなりキューがキューくつになっているときはどうしよう。・・・ダジャレですが。やっぱり上の通り完璧に処理を行いたいならsetTimeoutで、時間に厳しくいくならsetIntervalにすればいいのか。
そうなるとsetIntervalのタイマーリストの登録のタイミングってのがどうも微妙だ。キューにではないだろうけど、リストには大量に登録されちゃうんじゃないか?次の処理に移るってことは、現時点の処理をしているとき既に次の処理がキューに登録されているってことになるだろうし。・・・んー何にしてもわからん。
とにかく、ここらで考えても「めんどくせ」になっちゃうのでいろいろ試そう。
タイマーのブラウザ精度みたいな比較
いろいろ試してたらいろいろ気になることがいくつか出てきたので、それっぽいことを確かめる。ブラウザは、
- Firefox3.6
- Googole Chrome4.1
- Opera10.53
- IE8
これだけで。Safariは無し。Chromeが少しは参考に・・・なる?かな。OSはただのwindows7。一応アドオンだの拡張機能は切っといた。で、気になったのは次の5つ。
- 最小のミリ秒
- トータル時間のミリ秒の精度
- タイマー間隔の精度
- それぞれブラウザの小さな気づき
- 補足みたいなもの
- ブラウザのドーピング?
- これは私だけの環境かもしれないので。おまけ。
1つの環境の1つの結果にすぎないので話半分だろうが。
1.最小のミリ秒
setTimeout(func, xx)で、xxに入れることができる最小のミリ秒を調べる。ここはそれぞれブラウザによって違うらしい。
調べに使ったコード。たぶん無駄なことしていないと思う。
function test(count, ms) { var c = 1; var time = [new Date()*1]; var id = setTimeout(function() { time.push(new Date()*1); c += 1; if(c <= count){ setTimeout(arguments.callee, ms); } else { clearTimeout(id); var tl = time.length; var av = 0; for(var i = 1; i < tl; i++) { var n = time[i]-time[i-1]; // 各間隔ごとのミリ秒 av += n; //document.write(n+'<br>'); // ミリ秒のログ } alert(av/count); // 各間隔ごとのミリ秒の平均をalert } }, ms); } window.onload = function() { var id = setTimeout(function() { test(100, 1); // test(繰り返す回数, ミリ秒) clearTimeout(id); }, 3000); };
onloadのsetTimeoutは3秒ほど待たしてすっきりさせてから呼び出すためのもの。これしないとFirefox不安定だった。
最小のミリ秒のとり方については、まずタイマーによって呼び出される関数の間隔ごとに時間を取得して配列に入れる。そしてクリアしたあとに、そのインターバルの前後時間を引くことで各間隔のミリ秒がいくらか求めてます。あとはそれを回転数で平均化させalertしているという流れ。
設定しているsetTimeoutのミリ秒は1ミリ秒に設定。これはブラウザ側で調節してもらうため。で、100回まわしてます。1000も10000もそう変わらなかったので。
下がalert(av/count)での平均結果。msはmsec(ミリセカンド)のことで、1/1000秒です。かなりこんまい単位なので、そう気にしても仕方がない数値ではある。
・setTimeoutでのそれぞれ最小の平均ミリ秒(表グラフ1-1)
ミリ秒 | Firefox3.6.3 | Chrome4.1 | Opera10.53 | IE8 |
---|---|---|---|---|
ms | 15.59ms | 3.92ms | 2.48ms | 15.91ms |
・setIntervalでのそれぞれ最小の平均ミリ秒(表グラフ1-2)
ミリ秒 | Firefox3.6.3 | Chrome4.1 | Opera10.53 | IE8 |
---|---|---|---|---|
ms | 15.5ms | 3.92ms | 2.66ms | 15.91ms |
一応setIntervalもやってみた。コードは変えてあります。変わらんな。見比べてみるとキリの良いミリ秒の最小数値としてはそれぞれ、
・setITimeoutとsetIntervalの結果からの最小の平均ミリ秒(表グラフ1-3)
ミリ秒 | Firefox3.6.3 | Chrome4.1 | Opera10.53 | IE8 |
---|---|---|---|---|
ms | 16ms | 4ms | 3ms | 16ms |
このあたりかな。Firefoxは予測通りで、Chromeについては速いことはなんとなく知っていた。
JavaScriptのTimerは1/1000秒すなわちmsecの精度で、タイマーを動かすことができます。すなわち setInterval("test", 1)のようにタイマーを発動させると、1msec毎にtestという関数を呼び出すことができます。ただ、実際には、このTimerの精度は、 JavaScriptのエンジンの実装に依存していて、一般的にタイマーの最小精度は10~16msec程度になっているのが通例でした。16msecというと、約1/60秒なので、アニメーションするときにもこの程度の精度があれば十分だし、悪意のあるJavaScriptの暴走を止める意味でも 16msecくらいに留めておくのが一般的だというのが従来のブラウザの常識(?)だと勝手に解釈してたのですが、Chromeのタイマー精度は 1msecで動きます。
http://blog.drikin.com/2008/09/chrome.html
というわけで。
だからFirefoxとIEについては16msあたりをキープしてある。若干早足気味で15msに頭つっこんではいるけれど。Chromeは私の環境で1msは無理だった。
それぞれ最小のミリ秒は出たわけど足並みが違うから、やっぱりだいたいのブラウザで共通に動作させるために、遅い方にそろえて16msあたりから利用するのが良いのかどうかってことだけど、それでもブラウザ間でミリ秒を整えるのは難しいかもなあ。
2.トータル時間のミリ秒の精度
たとえば、setIntervalを100ミリ秒で100回関数を呼び出すまでにかかった時間はいくらか?ぴったり10000になるのか?
var c = 0; var begin = new Date()*1; var id = setInterval(function() { if((c+=1) >= 100) { alert(new Date()*1 - begin); clearInterval(id); } }, 100); // 9997だった
だいたい10000前後になる。10000とピタリとできるわけでもなく、多少のミリ秒の誤差は出るのが普通。じゃあそれの精度を計測しようってのが今回の内容。
使うコード。バランスが悪い不恰好な形になっちゃったけど。
var result = []; function test(count, ms, loop) { var lc = 1; (function a() { var c = 1; var begin = new Date()*1; var id = setInterval(function () { c+=1; if(c > count){ clearInterval(id); var end = new Date()*1; result.push(end-begin); // 経過時間を配列に if(lc < loop) { lc += 1; a(); } else { var rl = result.length; var av = 0; for(var i = 0; i < rl; i++) { av += result[i]; } alert(av/rl); // 平均トータル時間 //console.log(av); //console.log(result); } } }, ms); })(); } window.onload = function() { var id = setTimeout(function() { test(100, 100, 10); // test(繰り返す回数, ミリ秒, 計測回数) clearTimeout(id); }, 3000); };
上の例のコードは100msで関数を100回呼び出させた場合の経過時間を、10回分計測し平均化させてalertしています。ってなんか説明難しいな。
冒頭のコードで考えると。
var c = 0; var begin = new Date()*1; var id = setInterval(function() { if((c+=1) >= 100) { alert(new Date()*1 - begin); clearInterval(id); } }, 100); // 1回目 9986 // 2回目 9996 // 3回目 9987 . . . // 10回目 9999 // この10回分の数値を平均化して出そうと私はしているのだった。fin.
fin.
100回とかめんどうなんで10回ぐらいでよろしかろ。というか平均値なんて出してもほとんど意味ないんじゃないかって思ったけどもう書いちゃったからいいか。
じゃあ、いろいろ取っていこう。パターンがたくさんありすぎるから絞る。とりあえず16ms、30ms、50ms、80ms、そして100msで取ってみることにする。あえてキリ良い数値。それぞれのmsで100回繰りした時間を計り、それの10回分のものを平均させた表がこれ。
・setInterval(func, ms)のそれぞれ10回分の平均(表グラフ2-1)
望ましい値 | Firefox3.6.3 | Chrome4.1 | Opera10.53 | IE8 |
---|---|---|---|---|
1600(16ms*100) | 1601.5 | 1700.1 | 1749.6 | 2705 |
3000(30ms*100) | 3002.8 | 3000.3 | 3000.8 | 3120 |
5000(50ms*100) | 5007.5 | 5000.5 | 5000.1 | 6240 |
8000(80ms*100) | 8002.5 | 8000.4 | 7999.9 | 9360 |
10000(100ms*100) | 9999.5 | 10000.6 | 10000.4 | 10921.6 |
※望ましい値に近いほど精度が高いということになる
全体的にみて。まずIEは今日もIEはいつも通り元気で安心です。良い子ですね。
Chromeは実は16msで指定しても17msで繰り返すようになっていた。途中経過のログをみたらそうなっていた。なぜそうなのか知らないが。これはおそらくOperaも同じだろう。なので1700あたりの値が出る。
トータルでの精度としてはChromeとOperaが強い。Firefoxはこれを見る限りではそこそこ良さそうに思える。でもタイマー間隔のログそのものをみるとどうなんだろうね。この数値を見る限りでは、どうも微妙な気がする。こういったことは次で確かめてみる。
それからsetTimeoutについてはほとんど違わないだろうということでやらない。
3.タイマー間隔の精度
これは、たとえばsetInterval(func, 16)で設定したとしたら、16msずつfuncを呼び出すわけだけど、必ずしも16msとは限らない。15msだったり、17msだったりと多少の誤差が出てくる。そうでなければ上の(表グラフ2-1)の値がばらついたりするはずがない。
そこで今度はその設定したms毎に正確に関数を呼び出しているのかどうかって内容。
トータル的にはIEをのぞいてだいたい望ましい値であることは(表グラフ2-1)で確認できているけど、タイマー間隔そのものの精度はどうなのだろうね。
調べ方は基準値からの差を平均化させようと思ったけど微妙だし直接目で見た方が良いだろうというわけで普通にグラフ化する。googleさんのお力をお借りします。
調べに使ったコードについては「1.最小のミリ秒」でのものとほぼ同じ。
・setIntervalでの100*16msによるタイマー間隔(表グラフ3-1)
※縦軸がmsで横軸が回数。この場合16msで関数を100回呼び出した結果。
まっすぐなほどタイマー間隔の精度が高い、ということになり、逆に上下にブレブレなほど精度は低いということになる。IEがブレすぎて他のが見えないので若干薄く。Firefoxは表2-1の16msをトータルでみれば精度が高いように思えるが、このように波打っている。
よくみえないが17msでほぼまっすぐなChrome。そしてそのラインで波打っているOperaが気になるがまあいいや。ChromeとOperaは優秀で、Chromeは飛びぬけて精度が高い。これだと確認しづらいが、このときは全部17msだった。だから基準値と平行線。1ms上なんだけどこれには訳がある?かもしれない。
・setIntervalでの100*30msによるタイマー間隔(表グラフ3-2)
※縦軸がmsで横軸が回数。この場合30msで関数を100回呼び出した結果。
なんだがそっけないグラフになってしまったが、ChromeとOperaは基準値上のためほとんど良く見えない。精度が高くて基準値の線とかぶってる。
・setIntervalでの100*50msによるタイマー間隔(表グラフ3-3)
※縦軸がmsで横軸が回数。この場合50msで関数を100回呼び出した結果。
IEはあさっての方向に。やんちゃだなあ。Firefoxはブレている。ChromeとOperaは基準値の邪魔をして基準値の黒が見えない。若干Operaがブレているので、緑とピンクの見分けができる。
・setIntervalでの100*80msによるタイマー間隔(表グラフ3-4)
※縦軸がmsで横軸が回数。この場合80msで関数を100回呼び出した結果。
IEは基準値よりも上を行く神になりたいそうです。無邪気ですね。
Firefoxはなんでか下回っています。今回は(表グラフ2-1)のトータルの精度よりも若干下回る結果に。OperaはChromeが確認しやすくしてくれたのか若干ブレています。Chromeは基準値とほとんどかぶっています。
・setIntervalでの100*100msによるタイマー間隔(表グラフ3-5)
※縦軸がmsで横軸が回数。この場合100msで関数を100回呼び出した結果。
もう見慣れた感じで。Operaがちょっと不安定になってきている。
全体的にみて。Firefoxが細かい時間にルーズだったということがわかる。それでも(表グラフ2-1)の通り全体的なバランスは取れている。とにもかくにも全体的にバランスとれてりゃ細かいことは気にしなくていいんじゃね、という感じ。
しかし、どういう訳でこんなふうにバランス取れるんだろ?繰り返す回数を知っているわけでもないのに。それとも分かる?いや、おそらくどの回数でも値は整うようになっているんじゃないかとは思うが。
それはそれですごいけど、ChromeやOperaと比較すると単なるごまかしに見えなくもない。別にそんな細かい精度なんて求めるケースもそう多くないし、1〜10ミリ秒の誤差だからたいして気にすることではないのだけど。それでもChromeやOperaはすごいよって話。
IEは今日も元気で良い子だったので安心しました。
とりあえず一番驚いたのはChrome。すごいなー。Operaについては優秀なんだけどChromeのせいでパっとしない。全然すごいと思うんだけど。そんなわけで終わり。あとはおまけやら補足やら。
4.それぞれブラウザの小さな特徴
Chromeの1msの誤差
ChromeはsetInterval(func, 16)としたときに17msで繰り返し呼び出している。setTimeoutでも一緒。でもたとえばこれで手動リロードさせて値を出す形にすると、
var c = 0; var begin = new Date()*1; var id = setTimeout(function(){ if((c+=1) > 10) clearTimeout(id); else document.write((new Date()*1)-begin+'<br />'); },16); //17 //18 //16
と出てくる。だから16が出ないわけじゃない。ただ、繰り返させると17msになる。それが一番安定しているからなのか、もしくはたまたまこうなっちゃうのか。それを知る術はないけれど、これはOperaでも一緒。
トータルの精度を試した(表グラフ2-1)の16msもひっぱってくると、
・(表グラフ2-1)よりChromeとOperaの数値を強調
望ましい値 | Firefox3.6.3 | Chrome4.1 | Opera10.53 | IE8 |
---|---|---|---|---|
1600(16ms*100) | 1601.5 | 1700.1 | 1749.6 | 2705 |
どちらも17msにつっこんできる。何か共通している部分があるからこそこういう結果になっているんだろうし、別に気にすることでもないといえばその通りだが。
またChromeには16ms以外にもこういう結果になるミリ秒というのはある。適当に挙げていくと、
- 5ミリ秒のとき→6
- 6ミリ秒のとき→7
- 12→13
- 15→16
- 25→26
- 26→27
- 28→29
- 29→30
setIntervalでもsetTimeoutで繰り返しても一緒。
単に時間取得時の誤差であって、ブラウザそのものはしっかり16msやら上記のミリ秒で行われているかもしれない。そのように思ってはいるのだけど私の環境では一応トータルの精度で17ms記録しちゃったし、なんか突っ込んでおかないといかんよなということで。
だからそう気にすることじゃないと思う。
Firefoxのバランス間隔
これが一番きれい。
・FirefoxでsetIntervalによる20ms*100(表グラフ4-1)
バランス感覚は一品。素晴らしい。なんか不思議だなぁ。
IE的な、あまりにもIE的な
・IEでsetIntervalによる1ms*100(表グラフ4-2)
これは1ms。基準値は16msにしてあるけどsetInterval(func, 1)。なんだよくやってるじゃんIEと最初思った。じゃあ今度は(表グラフ3-1)でもあったけどsetInterval(func, 16)の場合を取り上げると。
・IEでsetIntervalによる16ms*100(表グラフ4-3)
改めてなんか違和感を感じる。どうやらIEは逆境に燃えるタイプのようです。
これを元にブラウザさんたちにインタビューしてきました。
IEさんいわく
「ほら、おれって逆境に強いタイプっしょ。こう、燃えるものがないとガーっといけないんだよ。だからこの結果仕方は無い。ああ、認めるよ。このときのおれは燃えてない。このときのおれは燃えてない。大事なので2回いいましたっと(笑) あのときは違ったんだけどな。まだ記憶に新しいブラウザ戦争のときっしょ。おれ、あのとき柄にもなくちょっと本気だしちゃったなー(笑) おれの熱いソウル(魂)がふつふつと煮て沸いて茹で上がっちゃったんだよな!ActiveXサイコー!なんか熱くなりすぎてIE6なんて独自にフロンティア(無駄拡張)しちゃったし。あとJSで画像のフィルタあるっしょ?あれ良くね?いやー、ちょっとあれ思いついたときおれ次世代の申し子じゃねとか勘違いしちゃったよ。 まあ勘違いかどうかはあのときと今を見比べてくれれば分かると思うけど(笑) 最近やっとおれに時代が追いついてきたつーか、まあおれがこの流れ作ったんだけどさ。結局それでおれも動きずらくなったんだけどね。というか他の方々のために動かないようにしていた、っていう優しさもあるんだけど(笑)。え?優しそうに見えないって?はは、こうみて実はおれ思いやりがあったりするんですって(爆) いやいや、信じてくださいよー、きっと一日おれにつきそってればその思いやりっつーか、男気ってのに気づくと思うんだわ。それ、あなたに気づかせたいなー。一日というか、今後ずっと一緒に・・・でもいいんだけどって、あっワリー!ごめんごめん!つい本音でちゃった(照) とりあえず今のとこ充電期間ってことで次のナインなおれに期待しといてください^^。Operaも真っ青にウサイン・ボルトっぽくなってますから!・・・速いって意味ね。ちょうど今逆境なときだし。こんなときしか本気だせないなんてこの性格どうにかしたいんだけど、もうあきらめてるよ。ははは。だってこれがおれのStyle(笑)だから」
次。
Firefoxさんいわく
「いるよねこういう勘違いしたやつ。まあアタシは違うけどね。アタシはね、一歩身を引いて状況を把握しながら適切な選択をするタイプだから。ガーって燃えたって仕方が無いよ。お前酸素だけで燃えられるかってのー(笑) だいたいチャラいんですよこいつ。ブラウザ戦争?はッ、あほらしい。実はね、あれはこいつのバックにいるミクロマグロソフトっていうミクロだかクロマグロだかそういう会社がいたんですよ(マイクロソフト)。それのおかげでこいつはスタンダードな地位を確立したんだけど、この勘違いブラウザは自分の実力だと思ってるんです。かわいそうでしょー(笑)ほーんと、かわいそぉ〜(深笑) だけどね、ホンモノってのは、爪を隠しておくものなんです。アタシのように・・・ね。そう、実はアタシまだホンモノの実力出していないから。世間様はChromeだのOperaだのうるさいけど、アタシまだ全力じゃないから。 ええ、だからまだ、隠している爪を立てたことがないんですよ(笑)。ウフフッ。だってアタシ争いごと嫌いだし。マニキュアはがれるのもいやだからね・・・ってちょっとここ突っ込むところー!!(笑) もーやだぁー、ちゃんと突っ込んでくださいよねっ。アタシ一人で恥ずかしいじゃないですかぁ(照)。そういう意味の爪じゃないんですよ。昔から「能あるトンビは鷹を隠す」っていうじゃないですかー(キリッ ・・・え?「能ある鷹は爪を隠す」じゃないか・・・って?あ、あ、アハハハ、そうそう!うまい!そうやって突っ込んでくればいいんですよ!やっと私のペースになれたかな?フフッ、ウーフフ!爪なんて単語ひとつも出さずに分かりやすくしたんですから、感謝してくださいよっ!(謎爆) とにかく私が爪を立てる前に相手が逃げるばっかりだから、トイメンで戦う機会がないんですよね。ChromeもOperaも逃げてばっかりだもの。そのくせ騒がれているからいやになっちゃう!・・・でもこればっかりは仕方がありません。私が本気出せない世の中ってのは少し物足りないけど。ウーフフーフフフッ。マニキュアもはがれずに済むし(笑) いずれ私を本気にさせる奴が現れるまではゆっくりやっていきます。まあもっとも、私が爪を立てたら地球なんてくちゃくちゃになっちゃうからこれからもなさそうなんだけど(苦笑)」
次。
ChromeさんとOperaさんとちゃっかりSafariさんの感想
「・・・(笑)」
また次回をお楽しみに。流れ上、Firefoxさんを陥れましたが普通に出来る子です。IE9はどうなるか知りませんが良い子なのでがんばってください。そうでないとSleipnirさんも困ってしまいます。関係ないけど国産て響きが良いですね。
5.ブラウザのドーピング?
最小のミリ秒ってのは次の通り。
・setITimeoutとsetIntervalの結果からの最小のミリ秒(表1-3)
ミリ秒 | Firefox3.6.3 | Chrome4.1 | Opera10.53 | IE8 |
---|---|---|---|---|
ms | 16ms | 4ms | 2.5ms | 16ms |
これはsetTimeout(func,1)とsetInterval(func,1)を行って得た結果をみて、キリの良い数値にしたものだった。
じゃあこれらにドーピングはいります。
・ドーピングした最小ミリ秒(表4-4)
ミリ秒 | Firefox3.6.3 | Chrome4.1 | Opera10.53 | IE8 |
---|---|---|---|---|
ms | 10ms | 4ms | 1ms | 16ms |
FirefoxとOperaが速くなっています。一体何をしたのかは置いといて、もうひとつドーピング効果。
Firefoxで20msというとこういう感じ。
・FirefoxでsetIntervalによる20ms*100(表4-1)
これにドーピングはいります。
・ドーピングしたFirefoxでsetIntervalによる20ms*100(表4-5)
という具合。バランス感覚がなくなってるでえぇぇぇ。
そう取り立てるほどのものでないかもしれないけど、これはChromeと一緒に立ち上げているときに起きる現象。
たとえば、Firefoxを立ち上げている状態でChromeも立ち上げておく。するとFirefoxのタイマーの精度が高くなる。それでChromeを閉じると元のFirefoxに戻る。
FirefoxとOperaでも同じ現象が起きました。Firefoxの精度が高くなる。ChromeとOperaだったら、Operaの精度が高くなる。だから表4-4のOperaは、ChromeとOperaで試したもので1msになってる。IEは確認した限りではのけ者でした。落ち込むなよIE。
なんでこんなことになるのかわからないし、もしかしたら私の環境だけかもしれませんが。Chromeはドーピングブラウザでしたということで。
何にしても細かいことはわからないけど面白かったしまた苦労させられた。実はこの現象のおかげでブラウザ一つ一つ立ち上げては閉じ、立ち上げては閉じを繰り返していたよちくしょう。
比較終わり
実はこういう比較できるツールがある。
http://labs.gmo.jp/blog/ku/2007/12/post_6.html
自分でやることに価値があるのですか?わかりません><
気づいたらもとんでもなくスペースを使うはめになった。なげえ。こんなはずじゃなかった。なんだよインタビューって。何やってんだろ。
コードいろいろ
たいしたことない比較なんてせずにこっちやろうと思ってたらほとんどやることなくなった。こっちが主旨だったのに。
きほんてきなこと
Firefoxの場合。
setTimeout(function(){alert(2)},2); setTimeout(function(){alert(1)},1); // 2 // 1
Chromeの場合。
setTimeout(function(){alert(1)},2); setTimeout(function(){alert(2)},1); // 2 // 1
おそらくOperaも同じ結果になるんじゃないだろうか?そっちは確かめていないから分からないけれど、Chromeは1ms単位で判定する。Firefoxは16ms未満は16msぐらいまで引き上げて判定する。
※逆だったので修正
関数でくくったありがちなミス
function a() { setTimeout(function() { alert(1); }, 16) } (function () { setTimeout(function() { console.log(2); alert(2); }, 100); a(); })(); // 1 // 2
タイマー系を扱ったことがないと順番が2,1になるんじゃないかって思っちゃう。恥ずかしがることじゃないさ。さあ、そうやって勘違いしていた人は手をあげてみなさい。
・・・。
あれ私だけ?いやあ世の中の人は皆恥ずかしがりやだなぁ。まあ2,1の順番って知っていたけどね。あえてね、あえての手を挙げるという行動だったから。もちろん脇を隠しながらの挙手ね。迷ったけどね。いや、脇を隠すかどうかじゃなくて。
バカな己を演出しようかしまいか。ピエロになろうかどうか。結果的にこうなったけど、あえての結果だから。
そ、そのあたりのこと勘違いしないでよねっ!
・・・はい。ミリ秒を変えた。alertも入れた。これで表示されるタイミングを確認すると、
function a() { alert("It's me!"); setTimeout(function() { alert(1); }, 100) } (function () { setTimeout(function() { alert(2); }, 16); a(); })(); // It's me! // 2 // 1
It's me!が最初。あとはミリ秒順。どうキューに追加されているのかってのが分かる。これは最初の方の小難しい話で納得できた。
時間でsetIntervalを制御
カウントの変数を用意せずに時間で制御。こっちがメジャーな方法らしい。どの環境でも必ずそのミリ秒通りではない。そういうわけで、時間取って時間で制御するようにする。こうすればロースペックなPCにもやさしいみたい。
var duration = 3000; var begin = new Date()*1; var id = setInterval(function() { var now = new Date()*1; var time = now-begin; if(duration < time){ clearInteral(id); alert(time); } }, 100);
アニメーションさせるとしたら移動距離とかまぜてするかな。でもこれピタリと止まらなそう。ちょいやり方違うかもしれん。
終わり
主旨がたったこんだけか。なにやってんだ。これじゃあおまけみたいだ。あれ?何を調べようとしていたのだろう。分からなくなった。
とりあえずこれだけブラウザ間でタイマーの精度が違うと、どのブラウザでも同じ速度で動作ってどうすりゃいいんだろう。たぶんどっかのアニメーションライブラリ見たらありそうなんだけどさすがに疲れた。またいつか。
こんなにスペース割いて結局得たものはとりあえずChromeはすごいっていう印象のみ。ああ、やっぱりこの感覚になると思った。なんともいえない脱力感がおそってくる。・・・闇が私を覆う。クソッ、右手がうずきやがるぜ・・・。暗黒の狭間よりなんたらかんたら以下略終わり。
余分三兄弟アニメーション
最後にいろいろお勉強のために作ったらスパゲティになったのでおいしくいただきました。それっぽい形でやってみたが結局ボロボロ取り繕いすぎ。もう面倒だ。
ロケーションバーにつっこんで動くのはChrome4.1とOpera10.53。Firefox3.6はFirebugのコンソールからで、Firebugはウィンドウとして開いてから実行してくだしあ。IE8はロケーションバーから無理。互換モードも無理。Safariはわかんね。PC貧弱だと重いかもなあ。それでもうまくできるようにって思ってたけど、途中で(10秒で)あきらめた。がんばった方です。
javascript:var board=function(){var a=document.createElement("div");a.style.width="100%";a.style.height="100%";a.style.position="fixed";a.style.border="0";a.style.margin="0";a.style.padding="0";a.style.top="0";a.style.left="0";a.style.opacity="0.7";a.style.filter="alpha(opacity=70)";a.style.backgroundColor="#000";a.style.zIndex="5000";a.className="yobun";return a}(),Yobun=function(a,b,c,d){this.left=c-b/2;this.top=d-b/2;this.elm=function(){var e=document.createElement("div");e.style.backgroundColor=a;e.style.width= b+"px";e.style.height=b+"px";e.style.position="Fixed";e.style.left=c-b/2+"px";e.style.top=d-b/2+"px";e.style.borderRadius=b/2+"px";e.style.WebkitBorderRadius=b/2+"px";e.style.MozBorderRadius=b/2+"px";e.style.border="0";e.style.zIndex="10000";e.className="yobun";return e}()}; Yobun.prototype={flag:0,count:0,speed:20,endAnime:function(){if(confirm("\u3042\u201d\u3045\u306f\u3042\uff01\uff01")===true)for(var a=document.querySelectorAll(".yobun"),b=0;b<a.length;b++)document.body.removeChild(a[b])},createSerif:function(){var a,b;a=document.createElement("div");a.appendChild(document.createTextNode("\u307f\u3093\u306a\u5927\u597d\u304d\u3063\uff01"));a.style.color="#fff";a.style.fontSize="60px";a.style.fontWeight="900";a.style.position="Fixed";a.style.zIndex="11100";a.style.opacity= "0";a.style.filter="alpha(opacity=0)";a.style.top=this.toY-130+"px";a.style.left=this.toX-120+"px";a.className="yobun";b=a.cloneNode(true);b.firstChild.nodeValue="\u4f59\u5206\u4e09\u5144\u5f1f\uff01";b.style.left=this.toX-200+"px";b.style.top=this.toY+300+"px";b.style.fontSize="100px";document.body.appendChild(a);document.body.appendChild(b);return{serif1:a,serif2:b}},createLabel:function(){var a=document.createElement("div");a.appendChild(document.createTextNode(label[this.flag]+"\u3067\u3059")); a.style.color="#fff";a.style.fontSize="30px";a.style.fontWeight="900";a.style.position="Fixed";a.style.zIndex="11000";a.style.opacity="0";a.style.filter="alpha(opacity=0)";a.style.left=this.toX+size/5.5+"px";a.style.top=this.toY+size/2.6+"px";a.className="yobun";document.body.appendChild(a);return a},displayStr:function(){var a=this,b=this.createLabel(),c=5,d=setInterval(function(){c+=5;b.style.opacity=c/100;b.style.filter="alpha(opacity="+c+")";if(c===100){c=5;clearInterval(d);Yobun.prototype.flag+= 1;if(a.flag===label.length)d=setTimeout(function(){clearTimeout(d);var e=a.createSerif();d=setInterval(function(){c+=5;e.serif1.style.opacity=c/100;e.serif1.style.filter="alpha(opacity="+c+")";if(c===100){clearInterval(d);c=5;d=setTimeout(function(){clearInterval(d);d=setInterval(function(){c+=5;e.serif2.style.opacity=c/100;e.serif2.style.filter="alpha(opacity="+c+")";if(c===100){clearInterval(d);d=setTimeout(function(){clearInterval(d);a.endAnime()},3E3)}},20)},400)}},20)},800)}},20)},distanceCalc:function(a){var b= this.left-this.toX,c=this.top-this.toY;b/=a;c/=a;b=b<0?b*-1:-b;c=c<0?c*-1:-c;return{x:b,y:c}},move:function(a,b){var c=this;this.left+=a.x;this.top+=a.y;this.elm.style.left=this.left+"px";this.elm.style.top=this.top+"px";this.elm.style.opacity=(100-this.count*(50/b))/100;this.elm.style.filter="alpha(opacity="+(100-this.count*(50/b))+")";this.count+=1;if(b==this.count){clearInterval(this.id);this.elm.style.elft=this.toX+"px";this.elm.style.top=this.toY+"px";var d=setTimeout(function(){clearTimeout(d); c.displayStr()},50)}},start:function(a,b,c,d){var e=this;this.toX=c;this.toY=d;var f=b/this.speed,g=this.distanceCalc(f);document.body.appendChild(this.elm);this.elm.style.zIndex=1E4+a+"";var h=setTimeout(function(){e.id=setInterval(function(){e.move(g,f)},e.speed);clearTimeout(h)},a*500)}};var label=["\u8102\u80aa","\u7cd6\u5206","\u5869\u5206"],size=170; (function(){var a,b=window.innerWidth||document.documentElement.clientWidth,c=window.innerHeight||document.documentElement.clientHeight;if(document.compatMode==="BackCompat"){b=document.body.clientWidth;c=document.body.clientHeight}document.body.appendChild(board);a=["red","yellow","blue"];for(var d=0;d<3;d++)a[d]=new Yobun(a[d],size,b*((25*d+25)/100),c*0.85);a[0].start(1,1E3,b*0.5-size*0.1,c*0.5-size*0.1);a[2].start(2,1E3,b*0.5-size*0.9,c*0.5-size*0.1);a[1].start(3,1E3,b*0.5-size/2,c*0.5-size*0.75)})();void(0);
余分三兄弟にいつでも出会えます。寂しがりやに便利です。環境にもやさしいです。ブックマークレットにしとくのもいいかもしれません。私は絶対使いません。
何にしても己の愚かさというのは事後に気が付くものです。・・・私は余分なことしかしていないじゃないか!できればこのCMみたく「あ”ぅはあ!」とかって夢が覚めたらいいのになうふふ、ウフフフフ。
・・・あ”ぅうはああ!!
あー夢だった!良かった!余計なことしてない!みんなだいすきよぶんさんきょうだーい!(現実逃避)
タイマー系参考URL
適当に探して見つけたもの。
- JavaScriptのsetInterval関数の意味を正確に理解するための1つの説明 - 風と宇宙とプログラム
- 実践JavaScript - アニメーション#1 - 株式会社ALBERT 勉強会資料
- Kazuho@Cybozu Labs: setTimeout をオブジェクト指向にしてみる
- JavaScript を学ぶ際に一番重要なのに、誤解されがちな setTimeout 系の概念 - IT戦記
- 複雑で重くなった JavaScript を超高速化する方法。 - IT戦記
- 404 Blog Not Found:javascript - ページはいつ再描画されるか
- Life is beautiful: Javascript雑学:SetTimeoutについて知っておくべき事
- Chromium (Chrome) のソースを読む - NyaRuRuが地球にいたころ
- [D] エンジニアからみたChromeの中身
- 74 http://pipes.yahoo.com/pipes/pipe.info?_id=faa858a20082ef6d25ad27557e37e011
- 32 http://pipes.yahoo.com/pipes/pipe.info?_id=3572f9da2c8db3951cc02c59f68f43ba
- 31 http://pipes.yahoo.com/pipes/pipe.info?_id=0kJqAOKW3RGniq6n1ZzWFw
- 31 http://reader.livedoor.com/reader/
- 14 http://www5b.biglobe.ne.jp/~nnaro/brainstorm.html
- 11 http://blog.livedoor.jp/dankogai/
- 10 http://b.hatena.ne.jp/
- 10 http://www.google.co.jp/reader/view/
- 8 http://b.hatena.ne.jp/entry/d.hatena.ne.jp/sandai/20100513/p1
- 8 http://d.hatena.ne.jp/