在 JavaScript 中優雅的提取循環內的數據

翻譯:瘋狂的技術宅html

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

在本文中,咱們將介紹兩種提取循環內數據的方法:內部迭代和外部迭代。es6

循環

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

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 行)。函數

若是你發現循環內的某些數據(迭代文件)有用,但又不想記錄它,那應該怎麼辦?oop

內部迭代

提取循環內數據的第一個方法是內部迭代ui

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)調用 callbackspa

外部迭代

內部迭代的替代方案是外部迭代:咱們實現了一個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() 會調用咱們(「推」給咱們)。而這一次,換咱們來調用它了(「拉」過來)。code

請注意,在生成器中,必須經過 yield* 進行遞歸調用(第A行):若是隻調用 logFiles() 那麼它會返回一個iterable。但咱們想要的是在該 iterable 中 yield 每一個項目。這就是 yield* 的做用。

生成器有一個很是好的特性,就是處理過程可以與內部迭代同樣互鎖:每當 logFiles() 建立另外一個 filePath 時,咱們可以當即查看它,而後 logFiles() 繼續。這是一種簡單的協做式多任務處理,其中 yield 暫停當前任務並切換到另外一個任務。

擴展閱讀

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

相關文章
相關標籤/搜索