簡體   English   中英

在異步模塊內部調用沒有回調的函數

[英]Calling a function without callback inside a Async module

我在異步文檔中對此示例有疑問

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.mapasync.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並行而不是串行地處理計算。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM