hitode909の日記

趣味はマリンスポーツですの日記です

マカロンブームが来ていて、前に住んでた家の横の店では10種類くらい売ってるので、近くを通りかかったときに、とりあえず1個ずつ全種類買う。たまに出社したら散歩がてらマカロンを買いに行く。
今日も全種類買ったら、マカロンっておいしくないと思われがちですよね〜って店の人が話していて、今買ったところなのにそんな接客あるのか、っちょっとおもしろかった。

JavaScript 長いループ 分割

ブラウザで長いループや、重い処理をともなうループを回したいとき、同期的にJavaScriptを実行するとメインスレッドがブロックしてしまうので、ちょっとずつ細切れに分割して実行したい、ということがある。
昨日久しぶりに書いたら新たなパターンと出会ったので、これまでにどう書いてて今回どうなったかメモ。

setTimeoutする

以前(10年前とか)はこんなのをよく書いていた。
itemsがでかいArrayで、console.logがすごく重い処理だとして読んでください。

function iterateHeavyTask(items) {
  const startAt = new Date();
  while (items.length > 0 && new Date().getTime() - startAt < 10) {
    console.log(items.shift());
  }
  if (items.length > 0) {
    setTimeout(() => {
      iterateHeavyTask(items);
    }, 0);
  }
}


こんなにDateを作りまくって大丈夫なのか気になってたけど、試してみたら手元では秒間に700万回くらいループを回せてたので問題なさそうだった。

i=new Date();j=0; while(new Date().getTime() - i.getTime() < 1000) { j++; }; console.log(j);
7386752

requestIdleCallbackする

最近は、requestIdleCallbackがあるので、ブラウザに対して、落ち着いたときにやっといて、というのができる。
Safari, IE対応するときにはpolyfillを入れておくと良い。

function iterateHeavyTask(items) {
  const startAt = new Date();
  while (items.length > 0 && new Date().getTime() - startAt < 10) {
    console.log(items.shift());
  }
  if (items.length > 0) {
    requestIdleCallback(() => {
      iterateHeavyTask(items);
    });
  }
}

generatorを使う

ループ部分と処理部分が密結合してるのでgeneratorを使って書くとこうなり、ループと処理が分離できる。

function waitForIdle() {
  return new Promise((resolve) => {
    requestIdleCallback(resolve);
  });
}

async function* iterate(items) {
  let startTime = new Date();
  for (const item of items) {
    yield item;
    if (new Date().getTime() - startTime > 10) {
      await waitForIdle();
      startTime = new Date();
    }
  }
}

async function iterateHeavyTask(items) {
  const iterator = iterate(items);
  for await (const item of iterator) {
    console.log(item);
  }
}

isInputPending

generatorを使った版を書いた時点で、ループと処理が分離できて、これはよく書けた、と見せびらかしていたら、最近のChromiumにはisInputPendingというAPIがあり、ユーザー入力がpendingされているか判定できる、とid:mizdraに教えてもらえた。
Chromium 87以降なら実装されているので、実装されていたら使い、実装されてなかったらデッドラインによる判定に戻る形にする。

実装して、使用感を触ってみると、マウスを動かしたりスクロールしたりするとループが中断されて、JSがなにも仕事してないかのような使用感を得られた。
includeContinuousをfalseにするとマウス移動とかは拾ってくれないので、なにかの要素にホバーするとCSSで色が変わる、とかが動かなくなる。そのあたりは用途によって調整すればよい。

function waitForIdle() {
  return new Promise((resolve) => {
    requestIdleCallback(resolve);
  });
}

async function* iterate(items) {
  let startTime = new Date();
  const deadlineHasCome = () => (new Date().getTime() - startTime.getTime()) > 10;
  const hasInputPending = navigator.scheduling && navigator.scheduling.isInputPending;

  for (const item of items) {
    yield item;
    if (
      hasInputPending
        ? navigator.scheduling.isInputPending({ includeContinuous: true })
        : deadlineHasCome()
    ) {
      await waitForIdle();
      startTime = new Date();
    }
  }
}

async function iterateHeavyTask(items) {
  const iterator = iterate(items);
  for await (const item of iterator) {
    console.log(item);
  }
}


ループを細切れにするのはたまに書くのだけど、書くたびに、ブラウザの良いAPIがだんだん増えていてありがたい。
仕事で重い処理を書いてしまって困るというときには、DOMのAPIをがしがし触っていて重いということが多かったのだけど、永久にフィボナッチ数列を計算するとか、純粋に重い処理を実行したいときはWeb Workers APIを使うという手もあるので、そのうち試したい。
この調子でループの分割を一生書き続けていたい。

追記

new Date().getTime()ではなくDate.now()を使うとDateのオブジェクトを作らず済むので軽いはず、と指摘をもらえた。
試したところ手元では1.5倍くらい速いようだった。

i=Date.now();j=0; while(Date.now() - i < 1000) { j++; };
k=new Date();l=0; while(new Date().getTime() - k.getTime() < 1000) { l++; }
console.log(j, l, j/l);
11400689 7299605 1.5618227287640907

スタンフォード大学 マインドフルネス教室

心の社会がやってきていると感じているので読んでみた。
マインドフルネスというと怪しいイメージがあった(google:片岡鶴太郎 ヨガみたいな)けど、心が満ち足りた状態というか、本では、この瞬間に集中すること、と記されていた。目を閉じて桜の花の香りをかぐだけでマインドフルになれる、と序盤に書かれていて、そういうことね、と納得感があった。
失敗を恐れずビギナーになる、失敗や自分の弱さを認める、とか、人の話を聞く、とか、心を大切にしようという雰囲気の本。1on1とかアクティブリスニングとか、そういう文脈で出てくる話題も多かったので、まったくもって未知の分野というわけでもなくて、自然と読めた。
Team Geekとか好きな人ならすんなり読めると思う。

彼らは自分のハートに従おうとする。使命や目的を信じ、夢を抱く。だが、この夢というのは「私! 私! 私!」という夢ではなく、「私たち」という夢だ。自分は切り離された個人だと考えるのをやめた彼らは、自分が複雑に絡み合った他者らの大きな網の一部だと考えている。援助を受けるのにも差し出すのにも抵抗がなくオープンだ。

桃鉄9時間やった

昨日Switchの桃鉄を買ってみて、桃鉄やりながらピザでもつまもうと、ピザの出前を取って、妻と遊んでいた。
やってみたら熱中してしまって、終わり時を見失い、晩ごはんは冷凍の唐揚げとフライドポテトとじゃがりこで、24時まで遊んでいた。9時間遊んでいたことになる。
寝るときに目を閉じたら日本地図とレールが見えた。上に行ってカードもらったら?とかずっとしゃべっているので喉痛くなった。
これをやってると日本の地理に詳しくなれるし、どこにどういう特産品があるかわかって、良いコンテンツだと思う。電車に乗ってたびに出たくなる気持ちもちょっとわからないでもない。


このブログに、幼少期のハイチュウの話を書いてたと思ったのだけど書いてなかった。隣の家のよしきくんの家でよくスーパーファミコンやってたのだけど、うちにはゲーム機買ってもらえてなかったので、何をどうしたものか、よくわかってなかった。
ハイチュウが1個だけあって、よしきくんの家には3人の仲間たちが集まっていたので1個のハイチュウをどうやってシェアするか、という結論が3回ずつ噛んで、口から出して、次の仲間の口でも3回噛み、以下、再帰的にローテーションしていく、というものだった。それが、よしきくんの家での最大の思い出。

最近ずっと頭の中にこの曲が流れていて、本を読んでいてもこの曲が常時再生されているのでなかなか集中できず、英語の本読んでるときに日本語の曲が流れていくと相性が悪い。明日祝日だけど何か用事あるか、みたいな話をしているときに汚泥3Kに這いつくばるみたいな言葉が出てきそうになる。
www.youtube.com

どこに住むかという前に、どういう暮らしをしたいのかという話を解決する必要がありそう。
人生を通じて成し遂げたいことがなにかあるとして、それに近づくにはどこにどうやって住むのが一番良いのかという順で考えるべき。
治安の良さとか物価の低さとかはあくまで補助というか、お得なパラメータというだけで、それだけで決めることにはならないと思う。