Closed7

eventをPromiseで扱ってみる

うじまるうじまる

callback

JSを扱っているとcallbackがよく出てくる

document
  .getElementById('hoge')
  .addEventListener(() => console.log('this is callback'))

のような感じでEventHandlingでよく使う

うじまるうじまる

Promise

Promise オブジェクトは非同期処理の最終的な完了処理 (もしくは失敗) およびその結果の値を表現します。
MDNより

というように非同期処理の結果を表現するための値としてPromiseが使われる

よく使われるのは fetch API とか

fetch('url')
  .then(x => x.json())
  .then(x => console.log(x))

みたいな
callbackも同様に非同期の文脈で扱われることがある

var oReq = new XMLHttpRequest();
oReq.addEventListener('load', () => {
  console.log(this.responseText);
});
oReq.open('GET', 'url');
oReq.send();

みたいな

うじまるうじまる

callbackをPromiseに

Node.jsの標準ライブラリには非同期な処理でcallbackを使うことが多く、それをPromiseにしたくなるときがある

const fs = require('fs')
fs.readFile('/etc/passwd', (err, data) => {
  if (err) throw err;
  console.log(data);
});

これを

const fs = require('fs')

const readFile = (path) => new Promise((res, rej) => {
  fs.readFile(path, (err, data) => {
    if (err) {
        rej(err);
        return;
    }
    res(data)
  })
})

readFile('/etc/passwd')
   .then(x => console.log(x))

みたいな感じにしたくなる

うじまるうじまる

async/await

Promiseにしたモチベーションに async/await を使いたいというものがある。
これは、非同期な処理を同期的な書き方でできるもの

promise.then(x => promiseFunc(x)).then(x => console.log(x))

みたいな処理を

const x = await promise
const y = await promiseFunc(x)
console.log(x)

みたいに書ける

うじまるうじまる

本題

Eventも広義には非同期じゃない?ということでPromiseにしてみる
Promiseになったものがこちら

const event = (dom, eventName) => {
  const handler = {
    listener: null,
    emit(e) {
      if (this.listener) {
        this.listener(e)
      }
    },
    listen(fn) {
      this.listener = fn
    }
  }
  
  dom.addEventListener(eventName, e => handler.emit(e))
  
  return () => new Promise((res, rej) => {
    handler.listen(e => res(e))
  })
}

実装はとりあえずどうでも良くて使い方
以下の処理をやってみる

  • clickしたら透明度を0.5にする
  • 1000ms待つ
  • 透明度を1にする

css animation使えばいいじゃんとかは言っちゃいけない

比較のために普通に addEventListener を使って

const b = () => {
  const target = document.getElementById('target')
  let flag = false
  target.addEventListener('click', async () => {
    if (flag) {
      return
    }
    
    target.style.opacity = "0.5"
    flag = true
    setTimeout(() => {
      flag = false
      target.style.opacity = "1"
    }, 1000)
  })
}

b()

今が透明な状態かを示す flag という変数がいる
次に使ってみたやつ

const sleep = ms => new Promise(res => setTimeout(() => res(), ms))

const a = async () => {
  const target = document.getElementById('target1')
  const click = event(target, 'click')
  
  while(true) {
    await click()
    target.style.opacity = "0.5"
    await sleep(1000)
    target.style.opacity = "1"
  }
}

a()

sleepn ms 待ってくれるasync関数

while(true) {} で囲んでるのは何回も(無限に)やって欲しいから
見てわかるように状態がなくなった

うじまるうじまる

感想

言うほど欲しくなかったかも
ゲームとか作るときには便利そう(?)
then でイベントをつなげるとRxみたいな気持ちになってくる

// 3回クリックしたらなにかする
await click().then(click).then(click)
// なにか
このスクラップは2020/12/23にクローズされました