这个递归代码是否可以在 NodeJS/Javascript 中使用 while/for 循环编写为迭代代码



我写了一段代码,它有两个参数,第一个是某个 URL,第二个是 URL 必须下载多少次的整数(我知道下载相同的 URL 没有意义一次又一次,但这段代码只是一个示例,在实际代码中,URL 是从数据库表中随机选择的(,到目前为止,代码是作为递归函数编写的。这是我当前的代码的样子,

const request = require("request");
function downloadUrl(url, numTimes) {
if (numTimes > 0) {
console.log(url, numTimes);
request.get(url, function (err, resp, buffer) {
if (err) {
return err;
}
console.log(`MimeType: ${resp.headers['content-type']}, Size: ${buffer.length}, numTimes: ${numTimes}`);
downloadUrl(url, --numTimes);
});
}
}
function main() {
downloadUrl('http://somerandomurl', 5); // the URL here might get picked randomly from an array or a table
}
main();

我想知道的是,这个递归代码可以使用 while 或 for 循环编写为迭代代码吗?我试过编写以下代码,

function downloadUrl(url, numTimes) {
for (let i = 0; i< numTimes; i++) {
request.get(url, function (err, resp, buffer) {
if (err) {
return err;
}
console.log(`MimeType: ${resp.headers['content-type']}, Size: ${buffer.length}, numTimes: ${numTimes}`);
});
}
}

但是这段代码似乎是并行执行的,这显然是并行执行的,因为在 Node 中.js异步代码不会等待语句完成,然后再继续执行下一条语句,这与 Java 等编程语言不同。

我的问题是,有没有办法编写迭代代码来表现得与我的递归代码完全相同?我的递归代码按顺序执行,其中 numTimes 变量递减 1,并按顺序从 5 打印到 1。

我已经尽力让我的问题保持清晰,但如果有什么不清楚或令人困惑的地方,请随时提问。

我猜您希望结束您的 http 请求以发出另一个请求,如果我错了,请纠正我,但您可以在您的方法中使用 await。

const request = require('request');

async function downloadUrl(url, numTimes) {
for (let i = 0; i< numTimes; i++) {
const objToResolve = await doDownload(url);
if(objToResolve.err){
console.log(`Error: ${objToResolve.err}, try: ${i}`);   
}else{
console.log(`Size: ${objToResolve.buffer.length}, try: ${i}`);
}
}
}
// wrap a request in an promise
function doDownload(url) {
return new Promise((resolve, reject) => {
request(url, (err, resp, buffer) => {
if (err) {
reject({err});
}else{
resolve({err, resp, buffer});
}
});
});    
}
// now to program the "usual" way
// all you need to do is use async functions and await
// for functions returning promises
function main() {
console.log('main chamado');
downloadUrl('http://www.macoratti.net/11/05/c_aspn3c.htm', 5);
}
main();

编辑: 通过考虑超时,您可以更好地处理您的请求

const request = require('request');

async function downloadUrl(url, numTimes) {
for (let i = 0; i< numTimes; i++) {
try{
const objToResolve = await doDownload(url);
if(objToResolve.err){
console.log(`Error: ${objToResolve}, try: ${i}`);   
}else{
console.log(`Size: ${objToResolve.buffer.length}, try: ${i}`);
}
}catch(timeout){
console.log(`Error: ${timeout}, try: ${i}`);  
}
}
}
// wrap a request in an promise
function doDownload(url) {
const timeout = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('timeout'));
}, 300);
});
const requestPromisse = new Promise((resolve, reject) => {
request({uri:url, timeout:3000}, (err, resp, buffer) => {
if (err) {
reject({err});
}else{
resolve({err, resp, buffer});
}
});
});
return Promise.race([timeout,requestPromisse]);    
}
// now to program the "usual" way
// all you need to do is use async functions and await
// for functions returning promises
function main() {
console.log('main called');
downloadUrl('http://www.macoratti.net/11/05/c_aspn3c.htm', 5);
}
// run your async function
main();

参考:节点中的同步请求.js

每个递归代码都可以转换为非递归代码:)那么递归的魔力是什么呢?它只是滥用调用堆栈作为部分结果的存储。实际上,您可以构建自己的堆栈。Javascript使这变得非常容易。 您可以使用一些数组来存储部分结果。

using shift() to remove the first item of an array.
Using pop() to Remove the last element of an array:
Using push() to add to the end of an array
Using unshift() to add to the beginning of an array
Using splice() to add elements within an array

因此,有了这些,构建自己的"url"堆栈非常简单。 Push 和 Pop 将是你最好的朋友。 而不是你的递归,只要你不能下载,就把URL推送到数组 如果可以下载,请从数组中弹出 URL。

数组的长度将为您提供堆栈计数器的所有时间。 如果数组的长度为 0 :),则作业完成 所以简单来说:如果你认识到要清理的"混乱"变得更深,请将其推到阵列中,如果你可以删除一些"混乱",请完成这个小工作并将其从阵列中弹出。 这与递归一样没有别的。但无需惹恼操作系统或解释器。在过去美好的时光里,这种调用堆栈非常有限。因此,这个自己的堆栈构建将打破这些限制。它也可能是更多的内存足够。因为您只存储真正需要的东西。

我明白你的要求 - 我想你正在寻找发电机。 基本上,你只需要一个受控循环,在第一个项目完全完成之前,你不会迭代到下一个项目。

我的意思是,在幕后,它基本上仍然只是一个递归函数 - 它只是将其包装起来,使其像一个顺序的受控循环。

最新更新