Как пользоваться asyn c, ждать и обещать? - PullRequest
0 голосов
/ 22 апреля 2020

Я строю веб-скребок, чтобы получать все пользовательские материалы на codeforces. Я не знаю много об асин c, подожди, обещания. Я использовал ax ios (на основе обещаний) для запроса codeforces и 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);
      })
  })

}

Я использую функцию очистки для получения 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 голосов
/ 22 апреля 2020

Так я бы сконструировал функцию getstatus с функцией ожидания asyn c

 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) {
    }
  }
 }
}             
0 голосов
/ 22 апреля 2020

forEach(callback) выполняет callback. Если callback возвращает обещание (ie, это асинхронная c функция), обещание не будет выполнено до вызова обратного вызова для следующего элемента массива.

Итак, в основном, вы не можете использовать функции asyn c внутри forEach ... Но вы можете использовать for-loop или Promise.all вместо этого!

Также fs.writeFile работает с syn c + callback, но там существует fs.promise.writeFile, который вместо этого использует обещания.

Вот функция очистки, которая должна работать лучше:

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) }
    }
  }
}
0 голосов
/ 22 апреля 2020

Проблема в том, чтобы использовать result.forEach. Попробуйте использовать простое for (пусть i = 0; i

Если это не сработает, попытайтесь вернуть что-нибудь в то время.

...