LoginSignup
3
1

More than 1 year has passed since last update.

配列に格納されている関数を間隔空けて実行したいし戻り値もほしい

Posted at

経緯

WEBスクレイピングやろうと思った訳ですよ。ただ「このページを定期的にクロールする」とかじゃなくて「わんさかあるページからわんさか情報取ってくる」ってやつで、流石にページロードの間隔空けないと問題が起こるなぁと思って。

setTimeout は戻り値がとれない

外部にコンテナ用意して入れるとか、そんなむりやりな方法もありますが、関数が戻り値を返すんだからそのままほしいじゃん。

実装(普通の関数)

// 関数を遅延実行できるようにするやつ
const lazyFunction = (f, ...args) => {
  return () => f(...args)
}

// 適当な関数
const log = (n) => {
  console.log(n)
  return n
}

// 適当な関数を5個実行できるようにする
const logs = [1,2,3,4,5].map(n => lazyFunction(log,n))

// 等間隔実行の関数
const intervaledExecute = async (fs, interval) => {
  const promises = fs.map((f, i) => new Promise((resolve, reject) => {
    // setTimeout を interval 伸ばしながら実行するようにする
    setTimeout(() => resolve(f()), interval * i)
  }))
  // Promiseは一気に解決しちゃう
  return await Promise.all(promises)
}

// 1秒毎に1、2、3、4、5って出てその後に[1,2,3,4,5]が出る
(async () => {
  res = await intervaledExecute(logs, 1000)
  console.log(res)
})()

実装(実行したいのがPromiseだった場合)

// ここはかわらない
const lazyFunction = (f, ...args) => {
  return () => f(...args)
}

// 適当なPromise関数
const logp = n =>  new Promise((resolve, reject) => {
  console.log(n)
  setTimeout(() => resolve(n), 500)
})

const logps = [1,2,3,4,5].map(n => lazyFunction(logp, n))

// 等間隔実行関数
const intervaledExecutePromises = async (ps, interval) => {
  const promises = ps.map((p,i) => new Promise((resolve, reject) => {
    // then で Promise の結果を取り出して外の resolve に渡す
    setTimeout(
      () => p().then((r, s) => resolve(r)),
      interval * i
    )
  }))
  return await Promise.all(promises)
}

// 0.5 秒毎に数字が出て最後に配列が出る
(async () => {
  res = await intervaledExecutePromises(logps, 500)
  console.log(res)
})()

力技だなぁ……なんかもーちょっと……スマートな書きかた無いかなぁ……

3
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1