20

I'm trying to understand how async/await works in conjunction together with promises.

Code

async function latestTime() {
  const bl = await web3.eth.getBlock('latest');
  console.log(bl.timestamp); // Returns a primitive
  console.log(typeof bl.timestamp.then == 'function'); //Returns false - not a promise
  return bl.timestamp;
}
const time = latestTime(); // Promise { <pending> }

Issue

As far as I understand, await should be blocking and in the code above it seemingly blocks returning an object bl with the primitive timestamp. Then, my function returns the primitive value, however the time variable is set to a pending promise instead of that primitive. What am I missing?

user3223162
  • 303
  • 1
  • 2
  • 6
  • 1
    that's what async functions do - read the [spec](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) – Jaromanda X Jul 14 '18 at 12:00
  • `await should be blocking` no - blocking code in javascript is a "bad idea"™ and async/await has nothing to do with blocking at all – Jaromanda X Jul 14 '18 at 12:01
  • 1
    *Every* `async function` returns a promise so that inside of it you can `await` other promises, that's the whole point – Bergi Jul 14 '18 at 12:02
  • Does this answer your question? [async/await implicitly returns promise?](https://stackoverflow.com/questions/35302431/async-await-implicitly-returns-promise) – Ivar Jul 17 '21 at 21:28

3 Answers3

27

Async prefix is a kind of wrapper for Promises.

async function latestTime() {
    const bl = await web3.eth.getBlock('latest');
    console.log(bl.timestamp); // Returns a primitive
    console.log(typeof bl.timestamp.then == 'function'); //Returns false - not a promise
    return bl.timestamp;
}

Is the same as

function latestTime() {
    return new Promise(function(resolve,success){
        const bl = web3.eth.getBlock('latest');
        bl.then(function(result){
            console.log(result.timestamp); // Returns a primitive
            console.log(typeof result.timestamp.then == 'function'); //Returns false - not a promise
            resolve(result.timestamp)
        })
}
Theodor Losev
  • 346
  • 3
  • 3
  • Thank you for the descriptive answer, I obviously missed the point. – user3223162 Jul 14 '18 at 12:19
  • 2
    Just a side note: There are a couple of issues with that translation of the `async` function. It's okay in very broad conceptual terms, but doesn't handle rejection correctly, has a syntax error, and gives a very surprising name (`success`?!) to the second argument passed to the promise executor (the function to use to **reject** the promise)... :-) – T.J. Crowder Aug 19 '20 at 12:50
17

An async function always returns a promise. That's how it reports the completion of its asynchronous work. If you're using it in another async function, you can use await to wait for its promise to settle, but in a non-async function (often at the top level or in an event handler), you have to use the promise directly, e.g.:

latestTime()
.then(time => {
    console.log(time);
})
.catch(error => {
    // Handle/report error
});

If you're doing this at the top level of a JavaScript module, some environments now support the upcoming top-level await in modules:

const time = await latestTime();

JavaScript engines are getting support for top-level await, and Webpack has experimental support for it, for instance.


Here's a rough translation of your async function in explicit Promise terms:

function latestTime() {
    return new Promise((resolve, reject) => {
        web3.eth.getBlock('latest')
        .then(bl => {
            console.log(bl.timestamp);
            console.log(typeof bl.timestamp.then == 'function');
            resolve(bl.timestamp);
        })
        .catch(reject);
    });
}

Some important notes on that:

  • The function you pass to new Promise (the promise executor function) gets called synchronously by new Promise.
    • Which is why the operation starts, web3.eth.getBlock is called synchronously to start the work.
  • Any error (etc.) thrown within the promise executor gets caught by new Promise and converting into a promise rejection.
  • Any error (etc.) thrown within a promise callback (like the one we're passing then) will get caught and converted into a rejection.
T.J. Crowder
  • 959,406
  • 173
  • 1,780
  • 1,769
9

async function will return Promise anyway. Return value will be `Promise, so in your case it will be:

async function latestTime(): Promise<some primitive> {
  const bl = await web3.eth.getBlock('latest');
  return bl.timestamp;
}

So, further you can use it function like:

const time = await latestTime();

But for achieving general view about async/await feature it will be better to read documentation.

Oleksii
  • 1,498
  • 19
  • 25