Node.js脚本静默失败?

时间:2018-09-29 02:38:06

标签: javascript node.js asynchronous callback promise

我编写了一个Node.js脚本,该脚本使用downloadaxiosfs模块从Federal Register提供的JSON中提取网址,然后下载相关的PDF文件。但是,该脚本通常无法下载所有PDF。

无论出于何种原因,我的脚本都会“死机”,然后下载所有PDF文件。意思是,它开始时很棒(下载了70、80个文件),但随后停滞了。它不会触发我的捕获块,也不会以任何方式失败。它只是停止下载。

文件数取决于我所连接的wifi。但是,我从未能够完成代码并在代码中触发.then块。理想情况下,下载文件后,我想使用.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 here上,以防您想要安装并自己尝试。以下是使用简单英语进行的操作摘要:

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

出了什么问题?

编辑-

如下所示,该脚本运行了一段时间,但随后停顿了下来,不再下载其他文件...

enter image description here

2 个答案:

答案 0 :(得分:1)

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

下面共有3个解决方案

rateLimited ...这会触发每秒限制为给定请求数的请求

singleQueue ...一次只有一个请求,没有速率限制,只有一系列请求

multiQueue ...一次最多给定数量的“运行中”请求

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"));
}

我还对代码进行了一些重构,因此downloadPDFS仅使用promise-所有节点回调样式代码都放入writeFile

答案 1 :(得分:0)

正如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)
}