LoginSignup
12
13

More than 5 years have passed since last update.

Array::reduceRightで直列の非同期処理を簡潔に書く

Last updated at Posted at 2016-03-03

react-routerの人がgithub上で以下のようなコードを公開されていたのですが、内容を理解するのに時間が掛かったので解説します。

[1,2,3,4,5].reduceRight((next,arg)=>{
  return ()=>{
    console.log(arg)
    next()
  }
},()=>{
  console.log('all done')
})()

// 1
// 2
// 3
// 4
// 5
// all done

reduceRightは配列の値を、reduceRightのコールバック関数内で右から順番に処理します。

Array.prototype.reduceRight() - JavaScript | MDN

最後の行で})()としているように、reduceRightの戻り値を、関数として実行しています。

実行したのは、reduceRightで最後に返したarg=1を持つクロージャ関数です。
このクロージャ関数のnextは、reduceRightから渡されたarg=2を持つクロージャ関数です。
つまり、nextを実行すると、reduceRightreturnした関数を、直列に左から実行します。

arg=5のクロージャ関数には、本来nextは未定義ですが、これはreduceRightの第二引数initialValueから渡すことが出来ます。
その結果、コンソール上には"5"のあとに"all done"が表示されています。

閑話休題

冒頭に取り上げた実コードは以下の様なものでした。

npm-http-server/modules/index.js#L73-L94
const ResolveExtensions = [ '', '.js', '.json' ]

/**
 * Resolves a path like "lib/index" into "lib/index.js" or
 * "lib/index.json" depending on which one is available, similar
 * to how require('lib/index') does.
 */
function resolveFile(file, callback) {
  ResolveExtensions.reduceRight(function (next, ext) {
    return function () {
      statFile(file + ext, function (error, stat) {
        if (stat && stat.isFile()) {
          callback(null, file + ext)
        } else if (error && error.code !== 'ENOENT') {
          callback(error)
        } else {
          next()
        }
      })
    }
  }, callback)()
}

これを以下のようなtree上で実行すると、結果は以下のようになります

tree .
# .
# ├── bar.js
# ├── baz.json
# └── foo
resolveFile('foo',(error,found)=>{
  console.log(error,found) // null 'foo'
})
resolveFile('bar',(error,found)=>{
  console.log(error,found) // null 'bar.js'
})
resolveFile('baz',(error,found)=>{
  console.log(error,found) // null 'baz.json'
})
resolveFile('beep',(error,found)=>{
  console.log(error,found) // undefined undefined
})

おわりに

直列の非同期処理を簡潔に書く方法として、reduceRightを利用してみるのは如何でしょうか。
自分も、非同期でフォルダを上に登りながらファイルを検索したい時に、参考になりました。

climb-lookup-v1.0.0 - lookup the file while climbing to recursively. like a require().

12
13
0

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
12
13