在Async模块中调用没有回调的函数

问题描述 投票:1回答:1

我在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),但我不知道回调使用什么,回调定义在哪里?

javascript fs async.js
1个回答
-1
投票

你可以使用node的内置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 */)

Promises是现代JavaScript环境中的新并发原语。它们可以在(err, res) => { ... }格式的预期节点式错误优先回调的所有场景中轻松使用。这是async.map的情况。

承诺减轻了臭名昭着的“回调地狱”引发的一系列问题。如果由于某种原因您无法使用Promise并且必须使用节点式回调,那么这可能有助于您的理解。通过查看完整的工作示例,我学得最好,所以在不到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而不是回调编写的相同程序来使用Promise。正如您在下面看到的,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.allPromise.all存在,所以我们不必手工编写像asyncMap这样的东西。作为额外的好处,Promise.all并行处理计算,而不是串行处理。

© www.soinside.com 2019 - 2024. All rights reserved.