在最近的Node版本(8.0.0和更高版本)中,可以使用一個新的util.promisify
函數來獲得承諾。下面是我們如何可能會使用它:
// Of course we'll need to require important modules before doing anything
// else.
const util = require('util')
const fs = require('fs')
// We use the "promisify" function to make calling promisifiedReaddir
// return a promise.
const promisifiedReaddir = util.promisify(fs.readdir)
// (You don't need to name the variable util.promisify promisifiedXYZ -
// you could just do `const readdir = util.promisify(fs.readdir)` - but
// I call it promisifiedReaddir here for clarity.
function createFiles(countryCode) {
// Since we're using our promisified readdir function, we'll be storing
// a Promise inside of the readdirPromise variable..
const readdirPromise = promisifiedReaddir('./app/data')
// ..then we can make something happen when the promise finishes (i.e.
// when we get the list of directories) by using .then():
return readdirPromise.then(directories => {
// (Note that we only get the parameter `directories` here, with no `err`.
// That's because promises have their own way of dealing with errors;
// try looking up on "promise rejection" and "promise error catching".)
// We can't use a forEach loop here, because forEach doesn't know how to
// deal with promises. Instead we'll use a Promise.all with an array of
// promises.
// Using the .map() method is a great way to turn our list of directories
// into a list of promises; read up on "array map" if you aren't sure how
// it works.
const promises = directory.map(directory => {
// Since we want an array of promises, we'll need to `return` a promise
// here. We'll use our promisifiedReaddir function for that; it already
// returns a promise, conveniently.
return promisifiedReaddir(`./app/data/${directory}`).then(files => {
// (For now, let's pretend we have a "copy file" function that returns
// a promise. We'll actually make that function later!)
return copyFile(`./app/data/${directory}/en.yml`, `./app/data/${directory}/${countryCode}.yml`)
})
})
// Now that we've got our array of promises, we actually need to turn them
// into ONE promise, that completes when all of its "children" promises
// are completed. Luckily there's a function in JavaScript that's made to
// do just that - Promise.all:
const allPromise = Promies.all(promises)
// Now if we do a .then() on allPromise, the function we passed to .then()
// would only be called when ALL promises are finished. (The function
// would get an array of all the values in `promises` in order, but since
// we're just copying files, those values are irrelevant. And again, don't
// worry about errors!)
// Since we've made our allPromise which does what we want, we can return
// it, and we're done:
return allPromise
})
}
好吧,但是,有可能仍然可能被百思不得其解你..
什麼錯誤的幾件事情?我一直在說你不需要擔心他們,但是是很好了解他們。基本上,在承諾條款中,當一個錯誤發生在util.promisify
的函數內部時,我們說那個承諾拒絕。被拒絕的承諾的表現大體上與您預期錯誤的方式相同;他們會拋出錯誤信息並停止他們所承諾的任何承諾。因此,如果我們的promisifiedReaddir
調用中有一個拒絕,它將停止整個createFiles
函數。
那麼copyFile
函數呢?那麼,我們有兩個選擇:
使用別人的功能。無需重新發明輪子! quickly-copy-file
看起來是一個很好的模塊(加上它會返回一個承諾,這對我們很有用)。
自己編程。
編程它自己是不是太狠了,居然,但它需要比單純使用util.promisify
多一點點:
..而這裏的所有完成的代碼,這樣就可以自己審查:
const util = require('util')
const fs = require('fs')
const promisifiedReaddir = util.promisify(fs.readdir)
function createFiles(countryCode) {
const readdirPromise = promisifiedReaddir('./app/data')
return readdirPromise.then(directories => {
const promises = directory.map(directory => {
return promisifiedReaddir(`./app/data/${directory}`).then(files => {
return copyFile(`./app/data/${directory}/en.yml`, `./app/data/${directory}/${countryCode}.yml`)
})
})
const allPromise = Promies.all(promises)
return allPromise
})
}
function copyFile(from, to) {
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(from)
const writeStream = fs.createWriteStream(to)
readStream.pipe(writeStream)
writeStream.on('close',() => {
resolve()
})
writeStream.on('error', err => {
reject(err)
})
readStream.on('error', err => {
reject(err)
})
})
}
當然,這種實現是不完美。您可以通過查看其他實現來改進它 - 例如this one在發生錯誤時會破壞讀取和寫入流,這比我們的方法(不這樣做)要清潔一點。最可靠的方法可能會與我早些時候鏈接的the module!
我強烈建議你看funfunfunction's video on promises。它解釋了承諾如何工作,如何使用Promise.all
等;而且他幾乎可以肯定比我更好地解釋這個整體概念!
您將需要包裝'fs'方法,以使它們保證返回。或者使用像這樣的:https://github.com/normalize/mz –
看看這個遞歸目錄副本的例子:https://github.com/amaksr/nsynjs/blob/master/examples/node-copy- files/index.js – amaksr