翻譯:瘋狂的技術宅
在本文中,我們將介紹兩種提取迴圈內資料的方法:內部迭代和外部迭代。
迴圈
舉個例子,假設有一個函式 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
暫停當前任務並切換到另一個任務。
擴充套件閱讀
- Chapter “Iterables and iterators” in “Exploring ES6”.
- Chapter “Generators” in “Exploring ES6”.