问题描述
我在对此示例有 :
async.map(['file1','file2','file3'], fs.stat, function(err, results) {
// results is now an array of stats for each file
});
这个例子调用fs.stat
在(item,callback)
数组的每个元素中使用,但是我不知道回调的用途,回调定义在哪里?
1楼
您可以使用节点的内置util.promisify
和跳过需要async.map
和async.js共-
const { promisify } = require('util')
const fs = require('fs')
const files = ['file1', 'file2', 'file3']
Promise.all(files.map(promisify(fs.stat)))
.then(results => /* results is now an array of stats for each file */)
.catch(err => /* err is the first error to occur */)
承诺是现代JavaScript环境中新的并发原语。
它们可以轻松地用于所有需要以节点样式进行错误优先回调的场景,格式为(err, res) => { ... }
。
async.map
就是这种情况。
承诺减轻了因臭名昭著的“回调地狱”而引起的一系列问题。
如果出于某种原因您不能使用Promises,而必须使用节点样式的回调,那么这也许可以帮助您理解。
通过看完整的工作示例,我学得最好,因此,在不到50行中,我们实现了asyncMap
和示例async函数,供您查看每一部分如何发挥作用-
const delayedDouble = (x, callback) => setTimeout // delay a function ( () => // the function to delay callback // call the callback ( null // with no error , x * 2 // and the result ) , 1000 // delay the function 1000 milliseconds ) const asyncMap = (arr, func, cb) => { const loop = (res, i) => // make a named loop i >= arr.length // if i is out of bounds ? cb(null, res) // send no error and the final result : func // otherwise call the user-supplied func ( arr[i] // with the current element , (err, x) => // and a callback err // if there is an error ? cb(err, null) // send error right away, with no result : loop // otherwise keep looping ( [ ...res, x ] // with the updated result , i + 1 // and the updated index ) ) return loop // initialize the loop ( [] // with the empty result , 0 // and the starting index ) } asyncMap // demo of asyncMap ( [ 1, 2, 3 ] // example data , delayedDouble // async function with (err,res) callback , (err, res) => // final callback for asyncMap err // if an error occured ... ? console.error('error', err) // display error : console.log('result', res) // otherwise display result ) console.log('please wait 3 seconds...') // please wait 3 seconds... // <3 second later> // result [ 2, 4, 6 ]
上面, delayedDouble
总是通过调用callback(null, x * 2)
。
如果我们有一个有时会失败的函数,我们可以看到asyncMap
正确地传递了错误信息
const tenDividedBy = (x, callback) =>
setTimeout
( () =>
x === 0
// when x is zero, send an error and no result
? callback(Error('cannot divide 10 by zero'), null)
// otherwise, send no error and the result
: callback(null, 10 / x)
, 1000
)
asyncMap
( [ 1, 0, 6 ] // data contains a zero!
, tenDividedBy
, (err, res) =>
err
? console.error('error', err)
: console.log('result', res)
)
// error Error: cannot divide 10 by zero
如果没有错误,结果将按预期通过-
asyncMap
( [ 1, 2, 3, ]
, tenDividedBy
, (err, res) =>
err
? console.error('error', err)
: console.log('result', res)
)
// result [ 10, 5, 3.3333333333333335 ]
通过查看使用Promises而不是回调编写的同一程序,我们可以证明使用Promises的理由。
如下所示,Promise允许代码保持扁平化。
另请注意, asyncMap
如何无需关注代码的错误分支;
错误会自动冒出,并可以在异步计算的任何位置使用.catch
进行捕获-
const asyncMap = (arr, func) => { const loop = (res, i) => i >= arr.length ? Promise.resolve(res) : func(arr[i]).then(x => loop([...res, x], i + 1)) return loop ([], 0) } const tenDividedBy = x => x === 0 ? Promise.reject(Error('cannot divide 10 by zero')) : Promise.resolve(10 / x) asyncMap([1, 2, 0], tenDividedBy) .then(res => console.log('result', res)) .catch(err => console.error('error', err)) // Error: cannot divide 10 by zero asyncMap([1, 2, 3], tenDividedBy) .then(res => console.log('result', res)) .catch(err => console.error('error', err)) // result [ 10, 5, 3.3333 ]
这是一个不错的练习,所有内容,但是此答案的第一部分建议使用Promise.all
。
Promise.all
存在,因此我们不必手动编写诸如asyncMap
的东西。
另外一个好处是, Promise.all
并行而不是串行地处理计算。