Async not awaiting function before running

async/await arrow function
async/await vs promises
async/await try catch
nodejs async/await
can not use keyword 'await' outside an async function
await is only valid in async function
convert promise to async/await
await javascript

I'm trying to parse a specification website from saved HTML on my computer. I can post the file upon request.

I'm burnt out trying to figure out why it won't run synchronously. The comments should log the CCCC's first, then BBBB's, then finally one AAAA.

The code I'm running will not wait at the first hurdle (it prints AAAA... first). Am I using request-promise incorrectly? What is going on?

Is this due to the .each() method of cheerio (I'm assuming it's synchronous)?

const rp = require('request-promise');
const fs = require('fs');
const cheerio = require('cheerio');

async function parseAutodeskSpec(contentsHtmlFile) {
  const topics = [];
  const contentsPage = cheerio.load(fs.readFileSync(contentsHtmlFile).toString());
  const contentsSelector = '.content_htmlbody table td div div#divtreed0e338374 nobr .toc_entry a.treeitem';

  contentsPage(contentsSelector).each(async (idx, topicsAnchor) => {
    const topicsHtml = await rp(topicsAnchor.attribs['href']);
    console.log("topicsHtml.length: ", topicsHtml.length);
  });

  console.log("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");

  return topics;
}

Try it this way:

let hrefs = contentsPage(contentsSelector).map((idx, topicsAnchor) => {
  return topicsAnchor.attribs['href']
}).get()


let topicsHtml
for(href of hrefs){
  topicsHtml = await rp(href);
  console.log("topicsHtml.length: ", topicsHtml.length);
}

Now the await is outside of map or each which doesn't quite work the way you think.

Is it possible to use await without async in Js, before returning the variable, the function can be set with setTimeout(), so that the function waits for a few milliseconds. Use of async or await() function: This method can be used if the exact time required in setTimeout() cannot be specified. It has to be noted that it only makes the async function block wait and not the whole program execution. The code block below shows the use of Async Await together.

As @lumio stated in his comment, I also think that this is because of the each function being synchrone.

You should rather use the map method, and use the Promise.all() on the result to wait enough time:

const obj = contentsPage(contentsSelector).map(async (idx, topicsAnchor) => {
  const topicsHtml = await rp(topicsAnchor.attribs['href']);
  console.log("topicsHtml.length: ", topicsHtml.length);

  const topicsFromPage = await parseAutodeskTopics(topicsHtml)
  console.log("topicsFromPage.length: ", topicsFromPage.length);

  topics.concat(topicsFromPage);
})

const filtered = Object.keys(obj).filter(key => !isNaN(key)).map(key => obj[key])

await Promise.all(filtered)

console.log("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");

How to use Async Await in JavaScript., But if I absentmindedly call the function without await , I run into problems: let user = getFirstUser();. Even though my code doesn't await, it's not  Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the Await operator to the result of the call. The current method calls an async method that returns a Task or a Task<TResult> and doesn't apply the Await operator to the result.

Based on the other answers here I came to a rather elegant conclusion. Note the avoidance of async/await in the .map() callback, as cheerio's callbacks (and from what I've learned about async/await, generally all callbacks) seem not to honour the synchronous nature of await well:

async function parseAutodeskSpec(contentsHtmlFile) {
  const contentsPage = cheerio.load(fs.readFileSync(contentsHtmlFile).toString());
  const contentsSelector = '.content_htmlbody table td div div#divtreed0e338374 nobr .toc_entry a.treeitem';

  const contentsReqs = contentsPage(contentsSelector)
    .map((idx, elem) => rp(contentsPage(elem).attr('href')))
    .toArray();

  const topicsReqs = await Promise.all(contentsReqs)
    .map(req => parseAutodeskTopics(req));

  return await Promise.all(topicsReqs);
}

Async function without await in Javascript, Async functions enable us to write promise based code as if it were Running the above code gives the alert output as 27, it means that a promise was it only makes the async function block wait and not the whole program execution. work properly, we need to add async before the function firstAsync();  The suspension of an async method at an Await expression doesn't constitute an exit from the method, and Finally blocks don't run. The marked async method can itself be awaited by methods that call it. An async method typically contains one or more occurrences of an Await operator, but the absence of Await expressions doesn't cause a compiler error.

How to wait for a promise to finish before returning the variable of a , works only inside async functions let value = await promise ; because the engine can do other jobs in the meantime: execute other scripts, handle events, etc. We will get this error if we do not put async before a function. Running an async operation from a synchronous function Posted by Sanjeev October 22, 2014 October 22, 2014 Leave a comment on Running an async operation from a synchronous function One of those things that you almost never need, but if you need it, you need it.

Understand promises before you start using async/await, You can use await when calling any function that returns a Promise, Of course, the above example is not very useful, although it does serve a function, and we'​ve included the async keyword before the function keyword. This is possible because async-await is an abstraction on top of promises - async functions always return a promise, even if you don't explicitly declare them to do so. The await keyword can only be used inside functions that have the async tag. This also means that we cannot currently utilize it in the global scope.

How to use Async Await in JavaScript., If the return value of an async function is not explicitly a promise, it will be In this way, an async function without an await expression will run synchronously. In concurrentStart , if promise fast rejects before promise slow is  Since the return value of an async function is always wrapped in Promise.resolve, return await doesn’t actually do anything except add extra time before the overarching Promise resolves or rejects. The only valid exception is if return await is used in a try/catch statement to catch errors from another Promise-based function.

Comments
  • I would guess it is because of .each, but I'm not completely sure tbh. Have you tried converting the result of contentsPage(contentsSelector) into an Array (if it isn't already) and then use the for .. of .. syntax?
  • The problem could be this const topicsPage = cheerio.load(topicsHtml);. I think it should be something like const topicsPage = await cheerio.load(topicsHtml);
  • @KubwimanaAdrien Makes no difference if there's an await keyword or not
  • Looking at the source code for .each it looks like it's a synchronous function. There shouldn't be a need for the await keyword before calling .each
  • I think it has to do with the async call occurring in .each... see stackoverflow.com/questions/37576685/…
  • This is a great answer! I think .toArray is slightly more appropriate here? But Reading upon it, I'm unsure if it really makes a difference though, past readability. You were definitely on the right path about not using a Promise of any sort within the map() - it doesn't seem to honour the synchronicity that way. Is that a general callback thing, or just a quirk of cheerio?
  • It's not just cheerio, js map works the same way. The way I look at is the await will prevent the code below it from running before it comes back but not the iterator (higher order function?) itself from continuing.
  • Thanks, accepted as the answer as this achieved what I needed. I've "improved" it a little for my needs in my answer :)
  • Nope, throws an error. Promise.all takes an array of functions! map is synchronous too so I'm not sure I follow why that's important here
  • @NickBull Promise.all() takes an iterable of promises, not an iterable of functions. Since an async function always returns a Promise, this should in theory work. Maybe you could help by actually elaborating what the error is and which line it occurs on in the snippet you've provided.
  • @PatrickRoberts Sorry, you are right! I should have been more specific. I meant that this returns an Object, not a Promise
  • Hi @NickBull I edited my answer. Note the const filtered = ... that transforms the object generated by the map function into an array of promises.
  • @Hammerbot Sorry I just remembered about this question. Unfortunately I still believe that this has issues, as .map() does not handle asynchronicity well in the callback. See my answer for how I've gone about avoiding that