翻譯:瘋狂的技術宅html
在本文中,咱們將介紹兩種提取循環內數據的方法:內部迭代和外部迭代。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)調用 callback
。spa
內部迭代的替代方案是外部迭代:咱們實現了一個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
暫停當前任務並切換到另外一個任務。