Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test an Async function in JS - Error: "Did you forget to use await"

My code looks like this:

public getUrl(url) {
//returns URL
 ... }

public getResponseFromURL(): container {
  let myStatus = 4;
  const abc = http.get(url, (respon) => 
  const { statusCode } = respon;
  myStatus = statusCode;
  console.log('Inside callback' +myStatus);
  .on('error', (err) => {
  console.log('Things have gone wrong' + err);
  });
  console.log('ITS COMPLICATED' +myStatus);
  return new Container(status, body, header);
  }
}

The problem I am facing is because of the asynchronous nature of JS and the console.log('ITS COMPLICATED') gets executed before the one in the callback function. I am trying to have the first one executed before the last console.log!

I am using Async/Await like below:

  public timeoutPromise(time: any) {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(Date.now());
      }, time);
    });
  }

  public doSomethingAsync() {
    return this.timeoutPromise(1000);
  }

As a result changed my getResponseFromURL() to:

public async getResponseFromURL(): Promise<container> {
    this.myStatus = 7;
    console.log(0);
    await this.doSomethingAsync();
    console.log(1);
    const abc = http.get(url, (respon) => {
      const { statusCode } = respon;
      this.myStatus = statusCode;
      console.log('Inside Callback ' + statusCode);
    }).on('error', (err) => {
      console.log('Things have gone wrong ' + err);
    });
    await this.doSomethingAsync();
    console.log(2);
    await this.doSomethingAsync();
    console.log('Is it simple lalala ' + this.myStatus);
    await this.doSomethingAsync();
   }
}

The problem with doing this was if my container class (return type of getResponseFromURL()) is a container for status and body when I am testing this async function, before expect.getResponseFromURL().getStatus().toBe(200) would work.

Test looks like below:

  test('Async function', async () => {
    expect.assertions(1);
    const data = await ContainerGlobals.getResponseFromURL().getStatus();
    expect(data).toBe(207);
  });

Now I am getting error from .getStatus() and I am not sure how to bypass this error?

"does not exist on Promise"

like image 798
lanzchilz Avatar asked Jan 04 '19 17:01

lanzchilz


People also ask

What happens if you don't await async in JavaScript?

In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously. Code after each await expression can be thought of as existing in a . then callback.

Should I use async without await?

Rationale. Marking a function as async without using await or returning a value inside it can lead to an unintended promise return and a larger transpiled output. Often the function can be synchronous and the async keyword is there by mistake.

What happens when await fails JavaScript?

The await keyword before a promise makes JavaScript wait until that promise settles, and then: If it's an error, an exception is generated — same as if throw error were called at that very place. Otherwise, it returns the result.

When should I use async await JavaScript?

Async/Await makes it easier to write promises. The keyword 'async' before a function makes the function return a promise, always. And the keyword await is used inside async functions, which makes the program wait until the Promise resolves.


1 Answers

In the code above await is called on the result of calling getStatus on the result of calling ContainerGlobals.getResponseFromURL().

ContainerGlobals.getResponseFromURL() returns a Promise and immediately calling getStatus() on the Promise gives an error since getStatus "does not exist on Promise".

await needs to be called on the Promise returned by ContainerGlobals.getResponseFromURL(), and getStatus should be called on the result returned by await.

The quickest way to fix this is to throw parenthesis around the await:

test('Async function', async () => {
  expect.assertions(1);
  const data = (await ContainerGlobals.getResponseFromURL()).getStatus();
  expect(data).toBe(207);  // SUCCESS
});

...but you might want to split the await line into two lines for readability:

test('Async function', async () => {
  expect.assertions(1);
  const result = await ContainerGlobals.getResponseFromURL();  // let the Promise resolve
  const data = result.getStatus();  // call getStatus on the result
  expect(data).toBe(207);  // SUCCESS
});
like image 68
Brian Adams Avatar answered Oct 15 '22 15:10

Brian Adams