Javascript Node.js脚本是否静默失败?

Javascript Node.js脚本是否静默失败?,javascript,node.js,asynchronous,callback,promise,Javascript,Node.js,Asynchronous,Callback,Promise,我已经编写了一个Node.js脚本,它使用download、axios和fs模块从提供的JSON中提取URL,并下载相关的PDF文件。但是,脚本通常无法下载所有PDF 无论出于什么原因,我的脚本在下载所有PDF文件之前都会“暂停”。也就是说,它一开始很好(下载了70、80个文件),但后来又停滞不前了。它不会触发我的捕捉块,也不会以任何方式失败。它只是停止下载 文件数量因我使用的wifi连接而异。但是,我始终无法完成代码,并在代码中触发。然后块。理想情况下,我希望使用.then块来处理下载后的文件

我已经编写了一个Node.js脚本,它使用
download
axios
fs
模块从提供的JSON中提取URL,并下载相关的PDF文件。但是,脚本通常无法下载所有PDF

无论出于什么原因,我的脚本在下载所有PDF文件之前都会“暂停”。也就是说,它一开始很好(下载了70、80个文件),但后来又停滞不前了。它不会触发我的捕捉块,也不会以任何方式失败。它只是停止下载

文件数量因我使用的wifi连接而异。但是,我始终无法完成代码,并在代码中触发
。然后
块。理想情况下,我希望使用.then块来处理下载后的文件

代码如下:

// The callback function that writes the file...
function writeFile(path, contents, cb){
  mkdirp(getDirName(path), function(err){
    if (err) return cb(err)
      fs.writeFile(path, contents, cb)
  })
};

// The function that gets the JSON...
axios.get(`http://federalregister.gov/api/v1/public-inspection-documents.json?conditions%5Bavailable_on%5D=${today}`)
  .then(downloadPDFS)
  .catch((err) => {
    console.log("COULD NOT DOWNLOAD FILES: \n", err);
  });

// The function that downloads the data and triggers my write callback...
function downloadPDFS(res) {
  const downloadPromises = res.data.results.map(item => (
    download(item.pdf_url)
      .then(data => new Promise((resolve, reject) => {
        writeFile(`${__dirname}/${today}/${item.pdf_file_name}`, data, (err) => {
          if(err) reject(err);
          else resolve(console.log("FILE WRITTEN: ", item.pdf_file_name));
        });
      }))
  ))
  return Promise.all(downloadPromises).then((res) => console.log("DONE"))
}
我的项目在Github上,如果您想安装它并自己尝试的话。下面用通俗易懂的英语总结一下正在发生的事情:

该脚本从服务器获取JSON,该服务器包含所有126个PDF的URL。然后,它将这些URL的数组传递给同步
map
函数。每个URL都通过
下载
模块转换为承诺。该承诺隐式返回,并存储在
promise.all
包装器中。当下载承诺解决时(文档下载完成),我的自定义writeFile函数将触发,用下载的数据写入PDF文件。下载所有文件后,
Promise.all
包装器应解析。但事实并非如此

出什么事了

编辑--

正如您在下面看到的,脚本运行了一段时间,但是它只是暂停,不再下载任何文件…


正如Jaromanda指出的,这可能与API限制我的访问有关,而不是与脚本中的错误有关

我在脚本中添加了一个过滤器,以选择更少的数据,它可以正常工作。详情如下:

axios.get(`http://federalregister.gov/api/v1/public-inspection-documents.json?conditions%5Bavailable_on%5D=${today}`)
  .then(downloadPDFS)
  .then(() => {
    console.log("DONE")
  })
  .catch((err) => {
    console.log("COULD NOT DOWNLOAD FILES: \n", err);
  });

function downloadPDFS(res) {
  const EPA = res.data.results.filter((item) => {
    return item.agencies[0].raw_name === "ENVIRONMENTAL PROTECTION AGENCY"; //// THIS FILTER
  });

  const downloadPromises = EPA.map(item => ( //// ONLY DOWNLOADING SOME OF THE DATA
    download(item.pdf_url)
      .then(data => new Promise((resolve, reject) => {
        writeFile(`${__dirname}/${today}/${item.pdf_file_name}`, data, (err) => {
          if(err) reject(err);
          else resolve(console.log("FILE WRITTEN: ", item.pdf_file_name));
        });
      }))
  ))
  return Promise.all(downloadPromises)
}

如果这确实是一个速率问题,那么有几种方法可以解决它(取决于API的速率限制)

下面是一个解决方案中的3个解决方案

费率限制
。。。这会触发每秒限制在给定数量的请求

singleQueue
。。。一次一个请求,没有速率限制,只是一系列的所有请求

多队列
。。。一次最多只能处理给定数量的“正在运行”请求

const rateLimited = perSecond => {
    perSecond = isNaN(perSecond) || perSecond < 0.0001 ? 0.0001 : perSecond;
    const milliSeconds = Math.floor(1000 / perSecond);
    let promise = Promise.resolve(Date.now);
    const add = fn => promise.then(lastRun => {
        const wait = Math.max(0, milliSeconds + lastRun - Date.now);
        promise = promise.thenWait(wait).then(() => Date.now);
        return promise.then(fn);
    });
    return add;
};
const singleQueue = () => {
    let q = Promise.resolve();
    return fn => q = q.then(fn);
};
const multiQueue = length => {
    length = isNaN(length) || length < 1 ? 1 : length;
    const q = Array.from({ length }, () => Promise.resolve());
    let index = 0;
    const add = fn => {
        index = (index + 1) % length;
        return q[index] = q[index].then(fn);
    };
    return add;
};

// uncomment one, and only one, of the three "fixup" lines below
let fixup = rateLimited(10); // 10 per second for example
//let fixup = singleQueue;   // one at a time
//let fixup = multiQueue(6); // at most 6 at a time for example

const writeFile = (path, contents) => new Promise((resolve, reject) => {
    mkdirp(getDirName(path), err => {
        if (err) return reject(err);
        fs.writeFile(path, contents, err => {
            if (err) return reject(err);
            resolve();
        })
    })
});


axios.get(`http://federalregister.gov/api/v1/public-inspection-documents.json?conditions%5Bavailable_on%5D=${today}`)
    .then(downloadPDFS)
    .catch((err) => {
        console.log("COULD NOT DOWNLOAD FILES: \n", err);
    });

function downloadPDFS(res) {
    const downloadPromises = res.data.results.map(item => fixup(() => 
        download(item.pdf_url)
        .then(data => writeFile(`${__dirname}/${today}/${item.pdf_file_name}`, data))
        .then(() => console.log("FILE WRITTEN: ", item.pdf_file_name))
    ));
    return Promise.all(downloadPromises).then(() => console.log("DONE"));
}
const rateLimited=perSecond=>{
perSecond=isNaN(perSecond)| perSecond<0.0001?0.0001:perSecond;
常数毫秒=数学地板(1000/秒);
让承诺=承诺.解决(日期.现在);
const add=fn=>promise.then(lastRun=>{
const wait=Math.max(0,毫秒+lastRun-Date.now);
promise=promise.thenWait(等待)。然后(()=>Date.now);
回报承诺。然后(fn);
});
返回添加;
};
常量singleQueue=()=>{
让q=Promise.resolve();
返回fn=>q=q.then(fn);
};
常量多队列=长度=>{
长度=isNaN(长度)|长度<1?1:长度;
const q=Array.from({length},()=>Promise.resolve());
设指数=0;
const add=fn=>{
索引=(索引+1)%length;
返回q[index]=q[index],然后(fn);
};
返回添加;
};
//取消注释下面三行“fixup”中的一行,并且仅取消注释一行
let fixup=费率限制(10);//例如每秒10次
//设fixup=singleQueue;//一次一个
//设fixup=multiQueue(6);//例如,一次最多6个
const writeFile=(路径、内容)=>新承诺((解析、拒绝)=>{
mkdirp(getDirName(path),err=>{
如果(错误)返回拒绝(错误);
fs.writeFile(路径、内容、错误=>{
如果(错误)返回拒绝(错误);
解决();
})
})
});
axios.get(`http://federalregister.gov/api/v1/public-inspection-documents.json?conditions%5Bavailable_on%5D=${today}`)
。然后(下载PDF)
.catch((错误)=>{
log(“无法下载文件:\n”,错误);
});
函数下载PDF(res){
const downloadPromises=res.data.results.map(项=>fixup(()=>
下载(item.pdf\u网址)
.then(data=>writeFile(`${uu dirname}/${today}/${item.pdf\u file\u name}`,data))
.then(()=>console.log(“文件写入:”,item.pdf\u文件名))
));
返回Promise.all(downloadPromises)。然后(()=>console.log(“完成”);
}

我还对代码进行了一点重构,因此
下载PDF
仅使用承诺-所有节点回调样式的代码都放在
写文件中

您对这个问题的原始标题是
节点.js速率限制问题
-您认为您遇到一些未公开API的速率有问题吗?是的,就这么想出来了!谢谢你给我指明了正确的方向。我对API不太熟悉,我想这就是你被限制的原因吧?好像没办法知道我已经达到极限了,除非流量被切断?响应http状态将在4XX范围内,而不是2XX范围内-它真正深入到您的
下载
函数是如何编写的-阅读github以获得
下载
-不清楚它在何处/如何处理各种响应状态-可能是由它包含的另一个库完成的... 所以,是的,很难说到底发生了什么除了“限制”未公开API的类型之外,您有三种选择。。。1.最多并行发出
n
请求(虽然
n
是个谜),2。依次提出每个请求,3。限制为每秒
n
请求。。。。。。但是,如果不知道极限是什么,就很难绕过它。。。我有三个场景的代码。。。(1.最多
n
并行请求,2.串联请求尽可能快,3.串联请求限制在特定速率)-但不知道施加的限制我不知道