此问题已在此处有答案:
How do JavaScript closures work?(86回答)
2天前关闭。
好的,很抱歉代码太长了。这是一个蜘蛛,递归地从网站下载所有的html页面。我的问题是:为什么iterate
函数中的回调总是这个函数:
(err) => {
if (err) {
console.error(err);
}
console.log("All files downloaded");
}
这实际上允许它退出递归。我以为回调会像这样,但当然它会创建一个无限递归:
(err) => {
if (err) {
return callback(err);
}
iterate(index + 1);
}
const request = require("request");
const fs = require("fs");
const path = require("path");
const cheerio = require("cheerio");
function urlToFilename(url) {
const parsedUrl = new URL(url);
const hostname = parsedUrl.hostname;
const pathname = parsedUrl.pathname;
return pathname === "/"
? `${hostname.slice(0, hostname.lastIndexOf("."))}/${hostname.slice(
0,
hostname.lastIndexOf(".")
)}.html`
: `${hostname.slice(0, hostname.lastIndexOf("."))}/${pathname.slice(
0,
pathname.lastIndexOf(".")
)}.html`;
}
function getPageLinks(url, body) {
const $ = cheerio.load(body);
const linkObjects = $("a");
const links = [];
const urlObj = new URL(url);
linkObjects.each((i, elem) => {
const link = $(elem).attr("href");
try {
const linkObj = new URL(link);
if (linkObj.hostname === urlObj.hostname) {
links.push(link);
}
} catch (err) {
// console.error(err);
}
});
return links;
}
function saveFile(filename, contents, callback) {
fs.mkdir(path.dirname(filename), { recursive: true }, (err) => {
if (err) {
return callback(err);
}
fs.writeFile(filename, contents, callback);
});
}
function download(url, filename, callback) {
console.log(`Downloading ${url}`);
request(url, (err, response, body) => {
if (err) {
return callback(err);
}
saveFile(filename, body, (err) => {
if (err) {
return callback(err);
}
console.log(`Downloaded and saved: ${url}`);
callback(null, body);
});
});
}
function spiderLinks(currentUrl, body, nesting, callback) {
if (nesting === 0) {
return process.nextTick(callback);
}
const links = getPageLinks(currentUrl, body);
function iterate(index) {
if (index === links.length) {
return callback();
}
spiderNested(links[index], nesting - 1, (err) => {
if (err) {
return callback(err);
}
iterate(index + 1);
});
}
iterate(0);
}
function spiderNested(url, nesting, callback) {
const filename = urlToFilename(url);
fs.readFile(filename, "utf-8", (err, body) => {
if (err) {
if (err.code !== "ENOENT") {
return callback(err);
}
return download(url, filename, (err, body) => {
if (err) {
return callback(err);
}
spiderLinks(url, body, nesting, callback);
});
}
spiderLinks(url, body, nesting, callback);
});
}
spiderNested(process.argv[2], 1, (err) => {
if (err) {
console.error(err);
}
console.log("All files downloaded");
});
2条答案
按热度按时间xu3bshqb1#
我仍然认为学习闭包应该足够了,但这里有一个片段显示了
iterate()
函数的多个变体是如何同时存在的:输出中最相关的部分可能是这一部分:
我们在
iterate()
内部定义了一个callback
,因此调用它,它报告它是iterate()
,并使用undefined
回调。它来自递归参数。如果您仔细阅读代码及其输出,我想您也会理解原始代码。
oxalkeyp2#
在
spiderLinks
内部,当它调用spiderNested
下降到链接到的页面时,就会发生递归:这是一个不同的回调,所以当处理完该页面时,会调用
iterate
,这样下一个页面将被类似地分析。只有递归树的根使用输出完成消息的回调。