在异步模块中调用没有回调的函数
Calling a function without callback inside a Async module
我对 Async documentation 中的这个例子有疑问:
async.map(['file1','file2','file3'], fs.stat, function(err, results) {
// results is now an array of stats for each file
});
此示例调用 fs.stat
以在数组的每个元素中使用 (item,callback)
但我不知道回调使用什么,回调定义在哪里?
您可以使用节点的内置util.promisify
并跳过对async.map
和async.js[=的需要59=]一共-
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 */)
Promises 是现代 JavaScript 环境中新的并发原语。它们可以很容易地用于 所有 需要节点式错误优先回调的场景,格式为 (err, res) => { ... }
。 async.map
.
就是这种情况
Promises 减轻了由臭名昭著的 "callback hell" 引起的一系列问题。如果出于某种原因你不能使用 Promises 而必须使用节点式回调,也许这有助于你的理解。我通过查看完整的工作示例学习得最好,因此我们在不到 50 行的代码中实现了 asyncMap
和一个示例异步函数,让您了解每个部分如何发挥其作用 -
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 的理由。正如您在下面看到的,Promises 允许代码保持更扁平。还要注意 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
并行处理计算,而不是串行处理。
我对 Async documentation 中的这个例子有疑问:
async.map(['file1','file2','file3'], fs.stat, function(err, results) {
// results is now an array of stats for each file
});
此示例调用 fs.stat
以在数组的每个元素中使用 (item,callback)
但我不知道回调使用什么,回调定义在哪里?
您可以使用节点的内置util.promisify
并跳过对async.map
和async.js[=的需要59=]一共-
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 */)
Promises 是现代 JavaScript 环境中新的并发原语。它们可以很容易地用于 所有 需要节点式错误优先回调的场景,格式为 (err, res) => { ... }
。 async.map
.
Promises 减轻了由臭名昭著的 "callback hell" 引起的一系列问题。如果出于某种原因你不能使用 Promises 而必须使用节点式回调,也许这有助于你的理解。我通过查看完整的工作示例学习得最好,因此我们在不到 50 行的代码中实现了 asyncMap
和一个示例异步函数,让您了解每个部分如何发挥其作用 -
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 的理由。正如您在下面看到的,Promises 允许代码保持更扁平。还要注意 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
并行处理计算,而不是串行处理。