APIのプロミス化
module.exports = function(callbackBasedApi) {
return function promisified() {
const args = [].slice.call(arguments)
// プロミスオブジェクトを作成し、即座に返す
return new Promise((resolve, reject) => {
// promisified関数に渡された引数列の末尾に
// 処理結果通知用のコールバック関数を追加
args.push((err, result) => {
if (err) {
return reject(err)
}
if (arguments.length <= 2) {
resolve(result)
} else {
resolve([].slice.call(arguments, 1))
}
})
// 作成した引数列でcallbackBasedApiを呼び出し
callbackBasedApi.apply(null, args)
})
}
}
Iteratorと再帰による逐次処理
ウェブスパイダの改良
const path = require('path')
const utilities = require('./utilities')
const request = utilities.promisify(require('request'))
const mkdirp = utilities.promisify(require('mkdirp'))
const fs = require('fs')
const readFile = utilities.promisify(fs.readFile)
const writeFile = utilities.promisify(fs.writeFile)
// URLをダウンロードし、ファイルを保存
function download(url, filename) {
console.log(`Downloading ${url}`)
let body
return request(url)
.then(response => {
body = response.body
return mkdirp(path.dirname(filename))
})
.then(() => writeFile(filename, body))
.then(() => {
console.log(`Downloaded and saved: ${url}`)
return body
})
}
function spiderLinks(currentUrl, body, nesting) {
// 空のプロミスを生成(メソッドチェーンを構築するための開始点)
// このプロミスはundefinedとともに即座にresolveされる
let promise = Promise.resolve()
if (nesting === 0) {
return promise
}
let links = utilities.getPageLinks(currentUrl, body)
links.forEach(link => {
promise = promise.then(() => spider(link, nesting - 1))
})
return promise
}
function spider(url, nesting) {
let filename = utilities.urlToFilename(url)
return readFile(filename, 'utf8')
.then(
(body) => (spiderLinks(url, body, nesting)),
(err) => {
if (err.code !== 'ENOENT') {
throw err
}
return download(url, filename)
.then(body => spiderLinks(url, body, nesting))
}
)
}
/* -------------------------------------------------------------------------- */
// コマンドラインから受け取ったURLを引数として渡してspiderを呼び出し
spider(process.argv[2], 1)
.then(() => console.log('Download complete'))
.catch(err => console.log(err))
// node index <https://tetracalibers.net>
パターンの勘所
let tasks = [ /** タスク */]
let promise = Promise.resolve()
tasks.forEach(task => {
promise = promise.then(() => {
return task()
})
})
promise.then(() => {
console.log('全てのタスクが完了しました')
})
ループによる並行処理
ウェブスパイダの改良