在 JavaScript 中優雅的提取迴圈內的資料

前端先鋒發表於2019-03-01

翻譯:瘋狂的技術宅

原文:2ality.com/2018/04/ext…

在本文中,我們將介紹兩種提取迴圈內資料的方法:內部迭代和外部迭代。

迴圈

舉個例子,假設有一個函式 logFiles()

const fs = require('fs');
const path = require('path');

function logFiles(dir) {
  for (const fileName of fs.readdirSync(dir)) { // (A)
    const filePath = path.resolve(dir, fileName);
    console.log(filePath);
    const stats = fs.statSync(filePath);
    if (stats.isDirectory()) {
      logFiles(filePath); // (B)
    }
  }
}
logFiles(process.argv[2]);
複製程式碼

從 A 行開始的迴圈用來記錄檔案路徑。它是 for-of 迴圈和遞迴的組合(遞迴呼叫在 B 行)。

如果你發現迴圈內的某些資料(迭代檔案)有用,但又不想記錄它,那應該怎麼辦?

內部迭代

提取迴圈內資料的第一個方法是內部迭代

const fs = require('fs');
const path = require('path');

function logFiles(dir, callback) {
  for (const fileName of fs.readdirSync(dir)) {
    const filePath = path.resolve(dir, fileName);
    callback(filePath); // (A)
    const stats = fs.statSync(filePath);
    if (stats.isDirectory()) {
      logFiles(filePath, callback);
    }
  }
}
logFiles(process.argv[2], p => console.log(p));
複製程式碼

這種迭代方式與Array的 .forEach()類似:logFiles() 內實現迴圈並對每個迭代值(行A)呼叫 callback

外部迭代

內部迭代的替代方案是外部迭代:我們實現了一個iterable,可以用生成器幫助我們實現:

const fs = require('fs');
const path = require('path');

function* logFiles(dir) {
  for (const fileName of fs.readdirSync(dir)) {
    const filePath = path.resolve(dir, fileName);
    yield filePath;
    const stats = fs.statSync(filePath);
    if (stats.isDirectory()) {
      yield* logFiles(filePath); // (A)
    }
  }
}
for (const p of logFiles(process.argv[2])) {
  console.log(p);
}
複製程式碼

如果是內部迭代,logFiles() 會呼叫我們(“推”給我們)。而這一次,換我們來呼叫它了(“拉”過來)。

請注意,在生成器中,必須通過 yield* 進行遞迴呼叫(第A行):如果只呼叫 logFiles() 那麼它會返回一個iterable。但我們想要的是在該 iterable 中 yield 每個專案。這就是 yield* 的作用。

生成器有一個非常好的特性,就是處理過程能夠與內部迭代一樣互鎖:每當 logFiles() 建立另一個 filePath 時,我們能夠立即檢視它,然後 logFiles() 繼續。這是一種簡單的協作式多工處理,其中 yield 暫停當前任務並切換到另一個任務。

擴充套件閱讀

歡迎關注京程一燈公眾號:jingchengyideng,獲取更多前端乾貨內容。

相關文章