1. 程式人生 > 其它 >js非同步遞迴方式

js非同步遞迴方式

技術標籤:node.jsjavascript

js非同步遞迴方式

寫下自己的思考是個好習慣,可以總結自己的學習和智慧,鞏固知識,也可以讓自己不犯同樣錯誤。希望自己可以堅持下來。

問題

遞迴就是自己呼叫自己,然後通過呼叫自己得到的結果來計算當前結果

斐波那契數列的遞迴計算方式就是一種典型的例子

function feb (n) {
  if (n == 1 || n == 2) return 1
  return feb(n - 2) + feb(n - 1)
}

這裡有一個重要的地方在於:要得到呼叫自己的結果

那麼自己是一個非同步函式,就會出現得不到自己的結果這種情況

因為非同步函式會在同步程式碼執行完之後執行

也就是說,遞迴不起來了

解決方法

js 提供了 Promise 類,可以解決這個問題

假設我們要通過 fs.readdir() 這個非同步方法來遞迴讀取某個路徑下的所有檔名

我們可以先通過 Promisefs.readdir() 這個非同步方法封裝起來

new Promise((resolve, reject) => {
  fs.readdir(path, 'utf-8', function (err, data) {
    if (err) reject(err);
    else resolve(data)
  })
})

這樣一來,我們就可以通過 asyncawait

來想像同步一樣來呼叫這個非同步函數了

由於遞迴時我們會多次呼叫 fs.readdir() 函式,每次的訪問的路徑都是不一樣的,所以我們需要一個Promise工廠

function asyncReadDir (path) {
  return new Promise((resolve, reject) => {
    fs.readdir(path, 'utf-8', function (err, data) {
      if (err) reject(err);
      else resolve(data)
    })
  })
}

這樣一來我們就可以通過這個工廠函式,輕鬆的得到一個封裝後的 fs.readdir()

函數了

有了這個工廠函式和 asyncawait ,我們可以簡單實現一個遞迴操作實現需求

async function dfsFileNames (path) {
  const data = await asyncReadDir(path)
  const res = []
  for (const fileName of data) {
    res.push(fileName)
    if (!fileName.includes('.')) {
      res.push(await dfsFileNames(`${path}/${fileName}`))
    }
  }
  return res
}

注意,async函式 返回的結果是一個 Promise ,想要獲取結果可以通過 .then() 或者 async + await

完整程式碼

const fs = require('fs');

function asyncReadDir (path) {
  return new Promise((resolve, reject) => {
    fs.readdir(path, 'utf-8', function (err, data) {
      if (err) reject(err);
      else resolve(data)
    })
  })
}
async function dfsFileNames (path) {
  const data = await asyncReadDir(path)
  const res = []
  for (const fileName of data) {
    res.push(fileName)
    if (!fileName.includes('.')) {
      res.push(await dfsFileNames(`${path}/${fileName}`))
    }
  }
  return res
}

const fetchFileNames = dfsFileNames('./')
fetchFileNames.then(res => {
  console.log(res);
})