如何使用异步,等待和承诺?

时间:2020-04-22 13:42:31

标签: javascript node.js web-scraping promise async-await

我正在构建一个Web抓取工具,以使所有用户提交的内容都符合规范。 我对异步,等待,承诺一无所知。 我已经使用axios(基于Promise)请求代码强制,并使用cheerio来解析HTML。

app.post("/", (req, res) => {
  const usernameorhandle = req.body.userName;
  getstatus(usernameorhandle).then ( ()=> {
      var output = fs.createWriteStream(__dirname + '/Data/solutions.zip');
      var archive = archiver('zip', {
        zlib: { level: 9 } // Sets the compression level.
      });
      output.on('close', function() {
        console.log(archive.pointer() + ' total bytes');
        console.log('archiver has been finalized and the output file descriptor has closed.');
      });
      output.on('end', function() {
        console.log('Data has been drained');
      });
      res.attachment(__dirname + "/Data/Problems", 'Codeforces-Solutions');
      archive.pipe(res);
      archive.directory(__dirname + "/Data/Problems", 'Codeforces-Solutions');
      archive.finalize();
    }) })

我用来接受发帖请求。 我将所有解决方案都放在一个文件夹中并创建zip文件夹,然后将其发送到res。

下面是我的getstatus函数。

    async function getstatus(handle){
  return new Promise(async (resolve, reject)=> {
    console.log("HELLLLLLLOOOOOOOO");
    await axios.get("https://codeforces.com/api/user.status?handle=" + handle + "&from=1")
      .then(response => {
        if(response.data.status === 'OK'){
          let results = response.data.result;
          console.log("AAAAAAAAAAAAAAAAAAAAAAAa");
          scrape(results).then( () =>{
            console.log("DONE");
            resolve();
          })
          .catch(err => console.log(err));
          // resolve();
        }
        else console.log(submissions.comment);
      })
  })

}

我使用scrape函数获取HTML数据并将其放入名为“问题”的文件夹中。

async function scrape (results){
  console.log("inside scrape");
  //  console.log("HELLO");
  return new Promise( async (resolve, reject) => {
    await results.forEach(async (result)=> {
      if(result.verdict === 'OK'){
        await axios.get("https://codeforces.com/contest/" + result.contestId + "/submission/" + result.id)
        .then(solutionPage => {
          const $ = cheerio.load(solutionPage.data);
          const path = "/home/srujan/Desktop/crawlerapp/Data/Problems/" + result.problem.name + ".cpp";
           fs.writeFile(path, $('#program-source-text').text(), function(err){
            if(err){
              console.log(err);
            }
            else{
              console.log("Saved file");
            }
          })
        })
        .catch( error => {
          console.log("HTML PARSE ERROR" + error);
        })
     }
    })
    console.log("hey");
    resolve();

  })

问题是我正在

HELLLLLLLOOOOOOOO
AAAAAAAAAAAAAAAAAAAAAAAa
inside scrape
hey
DONE
saved file
saved file
...

浏览器在完成后下载,然后保存文件。 我是JS新手,不知道为什么会得到这个。

PS:我知道这是一个很长的问题。我尝试阅读很多有关此的内容。不正确地了解该怎么做。我复制粘贴了一些我不理解的代码,例如如何压缩文件夹。

3 个答案:

答案 0 :(得分:0)

问题是使用result.forEach 尝试使用没有异步的简单for(让i = 0; i

如果这不起作用,请尝试在then内返回任何内容。

答案 1 :(得分:0)

这就是我如何使用async异步构造getstatus函数

 async function getstatus(handle) {

    const response = await axios.get("https://codeforces.com/api/user.status?handle=" + handle + "&from=1")

    if(response.data.status === 'OK') {

      let results = response.data.result;

      try {
        await scrape(results);
        console.log("DONE");
      }
      catch(error) {
      }

    }
}

scrape相应地起作用...

const fs = require('fs').promises;

async function scrape (results) {
  results.forEach(async (result)=> {

   if(result.verdict === 'OK') {
    const solutionPage = await axios.get("https://codeforces.com/contest/" + result.contestId + "/submission/" + result.id)

    const $ = cheerio.load(solutionPage.data);
    const path = "/home/srujan/Desktop/crawlerapp/Data/Problems/" + result.problem.name + ".cpp";

    try {
      await fs.writeFile(path, $('#program-source-text').text())
      console.log("Saved file");
    }
    catch(error) {
    }
  }
 }
}             

答案 2 :(得分:0)

forEach(callback) 执行 callback。如果callback返回一个promise(即它是一个异步函数),则在对数组的下一个元素调用回调之前,promise将不会被解析。

因此,基本上,您不能在forEach中使用异步函数...但是您可以使用for循环或Promise.all来代替!

此外,fs.writeFile可以与同步+回调一起使用,但是存在fs.promise.writeFile却使用promises。

这是一个应该更好用的刮擦功能:

async function scrape(results) {
  for (const result of results) {
    if(result.verdict === 'OK') {
      const solutionPage = await axios.get("https://codeforces.com/contest/" + result.contestId + "/submission/" + result.id);
      const $ = cheerio.load(solutionPage.data);
      const path = "/home/srujan/Desktop/crawlerapp/Data/Problems/" + result.problem.name + ".cpp";
      try {
        await fs.promises.writeFile(path, $('#program-source-text').text());
      } catch(err) { console.log(err) }
    }
  }
}