0

I am trying to improve my skills with async, await. So I am trying to make an app that collects the prices of different flights in different periods and then it decides in which period the plane ticket is cheapest for personal use.

const puppeteerExtra = require("puppeteer-extra");
const pluginStealth = require("puppeteer-extra-plugin-stealth");
puppeteerExtra.use(pluginStealth());
const PCR = require("puppeteer-chromium-resolver");

const howLongStart = 7;
const howLongEnd = 8;

const fromDate = new Date("2023-07-15");
const toDate = new Date("2023-08-31");

const airport = "PDL";

let tickets = [];

for (let i = 0; i < howLongEnd - howLongStart; i++) {
  let howLong = howLongStart + i;

  let tempFromDate = new Date("2023-07-15");
  let tempFromD = new Date("2023-07-15");
  let tempToDate = addDays(tempFromD, howLong);

  async function ticketFirstMethod() {
    const ticketFirst = await searchFlight(airport, tempFromDate, tempToDate);
    tickets.push(ticketFirst);
  }

  ticketFirstMethod();

  while (addDays(tempToDate, 1) <= toDate) {
    tempFromDate = addDays(tempFromDate, 1);
    tempToDate = addDays(tempToDate, 1);

    async function ticketMethod() {
      let ticket = await searchFlight(airport, tempFromDate, tempToDate);
      tickets.push(ticket);
    }

    ticketMethod();
  }
}

let lowestTicket;

let lowest = Number.POSITIVE_INFINITY;
let highest = Number.NEGATIVE_INFINITY;
let tmp;
for (let i = tickets.length - 1; i >= 0; i--) {
  tmp = tickets[i][0];
  if (tmp < lowest) {
    lowest = tmp;
    lowestTicket = tickets[i];
  }
  if (tmp > highest) highest = tmp;
}

console.log(lowestTicket);

function addDays(date, days) {
  date.setDate(date.getDate() + days);
  return date;
}

async function searchFlight(airport, tempFromDate, tempToDate) {
  const stats = await PCR();
  const browser = await puppeteerExtra.launch({
    executablePath: stats.executablePath,
    headless: false,
  });
  const page = await browser.newPage();
  await page.goto(
    "https://www.pelikan.cz/cs/letenky/T:1,P:4000E_0_0,CDF:PRGMUCFRATXLVIE,CDT:C" +
      airport +
      ",R:1,DD:" +
      tempFromDate.getFullYear +
      "_" +
      tempFromDate.getMonth +
      "_" +
      tempFromDate.getDay +
      ",DR:" +
      tempToDate.getFullYear +
      "_" +
      tempToDate.getMonth +
      "_" +
      tempToDate.getDay +
      "/",
    { waitUntil: "networkidle2", timeout: 0 }
  );
  const cheapestPrice = await page.waitForSelector(
    "#flight-10000 > div:nth-child(1) > flights-flight:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > div:nth-child(3)"
  );
  const price = await page.evaluate((el) => el.textContent, cheapestPrice);
  const priceOnly = price.replace(/\D/g, "");
  const ticket = [priceOnly, page.url()];
  await browser.close();
  return ticket;
}

I have tried to put here an example of the code.

Can anyone please help me?

EXPECTED

Firstly I choose a period from when to when it should be searching for the ticket. Then I call searchFlight with this period of time to search for the ticket. The main thread will wait for the function to be processed and then the ticket is pushed to tickets.

BEHAVIOUR

The main thread will not wait and it continous so there is undefined ticket pushed to tickets.

I was trying to use the then method on the line where I am calling searchFlight function. In then method I put tickets.push(ticket). But that didn't work.

I was trying to search for fix but because I dont understand await, async that much I could not fix my code.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
DemonCZ
  • 3
  • 3
  • First off, remove the `(async () => { .... }()` wrapper. That's superfluous and getting in the way. The parent function is already `async` so the wrapper is not needed. – jfriend00 Dec 27 '22 at 16:23
  • There are a lot of superfluous functiions here defined inside loops. I'd just inline the code til it works, then add sensible abstractions only at that point. You're attempting to [return the response from an asynchronous call](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) without `await`. You can't de-async async code. – ggorlen Dec 27 '22 at 17:15
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – ggorlen Dec 27 '22 at 17:19
  • Also relevant: [Why is "Can someone help me?" not an actual question?](https://meta.stackoverflow.com/questions/284236/why-is-can-someone-help-me-not-an-actual-question) – ggorlen Dec 27 '22 at 17:20
  • If anything throws here, you'll hang your process. Always use `finally` blocks to close your browsers and other resources. You probably don't need a whole new browser for each search, just a new page, or a new navigation on the same page, even. – ggorlen Dec 27 '22 at 17:26

1 Answers1

1

First off, remove the (async () => { .... }() wrapper. That's superfluous and getting in the way. The parent function is already async so the wrapper is not needed.

Then, searchFlight is async so you need to await its result where you are calling it. And, you'll need to make it's parent function async so you can use that await.

const ticket = await searchFlight(airport, tempFromDate, tempToDate);

Then, you have to actually return a result from inside of searchFlight. Right now, you have no return result at the top level of that function.

I would suggest you do that by not mixing await and .then(). Just use await like this:

async function searchFlight(airport, tempFromDate, tempToDate){

    const stats = await PCR();
    const browser = await puppeteerExtra.launch({
        executablePath: stats.executablePath,
        headless: false
    });
    const page = await browser.newPage()
    await page.goto("...", {waitUntil: "networkidle2", timeout: 0})
    const cheapestPrice = await page.waitForSelector('...');
    const price = await page.evaluate(el => el.textContent, cheapestPrice);
    const priceOnly = price.replace(/\D/g, "");
    const ticket = [priceOnly, page.url()];
    await browser.close()
    return ticket;
}

And, please eliminate any use of var. One should only be using const or let in modern Javascript.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thank you so much for helping me. I have removed duplicated async wrapper and also you are right that I didn´t return the value. I wrote that line inside the launch function so I wasn´t returning anything. ....But still I have a problem with the await keyword that I wrote in this line const ticket = await searchFlight(airport, tempFromDate, tempToDate); as you suggested. I am getting error that I am using await keyword outside an async function and that is right I am not calling it from any function. – DemonCZ Dec 27 '22 at 16:41
  • oh you have mentioned it ... so i will just create an async method and put it inside – DemonCZ Dec 27 '22 at 16:44
  • It is still not working. Final result is written as undefined. Because the main thread continues and dont wait for the puppetter to open the page and search for the price on every call of the searchFlight. So at the end puppetter opens all the tabs at once and on every tab it is searching for the price. But that is to late, I need that on every call it will just wait for the price of the ticket in specific period and when there is result it can continue. – DemonCZ Dec 27 '22 at 16:53
  • I have also added the whole code so you can see the problem more easily. Also i apologize you for the mess in my code. – DemonCZ Dec 27 '22 at 16:57
  • 1
    @DemonCZ - You're trying to cheat here and that doesn't work. I see you created `ticketMethod` so you could make it `async`, but then you just call `ticketMethod()` all by itself. Of course that won't wait for anything as you don't pay attention to the promise it returns. Put ALL your code into an `async` function. Remove the `ticketMethod()` wrapper. In general, stop using `async` wrappers like your original question and like `ticketMethod()`. Put your whole logic into an `async` function so the whole logic can benefit from `await`. – jfriend00 Dec 27 '22 at 17:10
  • @DemonCZ - FYI, if you switch to using `ESM` modules with `import` and `export`, then you can use top level `await` too and won't have this issue in a newish version of nodejs. With a CommonJS module (which you are using), you can just make a top level function, mark it `async` and then call it at the top level using `.then()` to know when it's done and `.catch()` to handle errors. – jfriend00 Dec 27 '22 at 17:11
  • Thank you very much. I understand it more now. – DemonCZ Dec 27 '22 at 17:23