0

I am getting into Node, and being thrown into the world of async programming.

I thoroughly read this article on Mozilla's site (along with many other links teaching about the async and await keywords):

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await

I have a project I am starting that will require numerous database calls, one after another. Some queries will rely upon the previous one's results. That being said, I decided I better practice and write some testing code.

Here is my testing code:

var users, items;

const getData = async function(){
    // This is a DB call. Will take
    // a little bit of time to complete.

    setTimeout(() => {
        var queryResults = "A list of items";
        items = queryResults;
    }, 1000);

    return items;
}

const getUsers = async function(){
    setTimeout(() => {
        var queryResults = "A list of items";
        users = queryResults;
    }, 1000);

    return users;
}

const init = async function(){
    var itemSuccess = await getData();
    var userSuccess = await getUsers();

    console.log(`Here is your items: ${items}, and here are your users: ${users}`);
}

init();

My code failed with:

Here is your items: undefined, and here are your users: undefined

So naturally I headed back to Mozilla. I re-read the docs, and tried a few changes. Still, no success. Ultimately I ended up back at my original code above.

I cannot see why this is failing. I am simulating the time a query would take by using a timeout. If async does what it says, both functions should return their value, and then my console log should print out the correct results.

Does anyone see where I am going wrong?

Thank you.

John S.
  • 504
  • 1
  • 3
  • 18
  • You are not awaiting a promise. The `async` keyword cannot automatically convert callbacks to promises. You need to start with a promise. All the `async` keyword does is that it allows you to use `await` inside it. That is its only function. If you are not using the `await` keyword you don't need the `async` keyword so the `async` in `getData` and `getUsers` are completely useless and do nothing at all – slebetman May 26 '20 at 05:10

3 Answers3

3

await waits for a Promise to resolve.

// normal function that returns a promise
const getData = function(){
    // This is a DB call. Will take
    // a little bit of time to complete.
    return new Promise(resolve => {
      setTimeout(() => {
          var queryResults = "A list of data";
          resolve(queryResults);
      }, 1000);
    });
}

const getUsers = function(){
    return new Promise(resolve => {
      setTimeout(() => {
          var queryResults = "A list of users";
          resolve(queryResults);
      }, 1000);
    });
}

const init = async function(){
    var itemSuccess = await getData();
    var userSuccess = await getUsers();

    console.log(`Here is your items: ${itemSuccess}, and here are your users: ${userSuccess}`);
}

init();

An async function does 2 things.

  1. it makes it possible to use the await keyword to wait for promises.
  2. it makes the function automatically return a promise itself that wraps whatever the function returns

async function foo() {
  return 123;
}

const v = foo();
console.log(v instanceof Promise);
v.then(value => console.log(value));

A more normal database query might look something like this

async function dbQuery(queryUrl) {
  const res = await fetch(queryUrl);  // fetch returns a promise that resolves to a Response
  return res.json(); // res.json returns a promise that resolves to the body of response parsed as json.
}

async function main() {
  const films = await dbQuery('https://greggman.github.io/doodles/test/data/films.json');
  console.log(JSON.stringify(result, null, 2));
}

main();
gman
  • 100,619
  • 31
  • 269
  • 393
2

Show us your REAL code with the actual database calls in it and we can help you much more specifically, including showing you working code. Please resist the temptation to post questions with pseudo-code. Show real code and we can help you so much faster and better if you show us the real code.

await ONLY does something useful when you await a promise that resolves or rejects when something you are interested in completes or errors.

It has no magic to somehow know when a a setTimeout() or any other asynchronous operation is done doing whatever it does.

So, when you do this:

var itemSuccess = await getData();

And, getData() looks like this:

const getData = async function(){
    // This is a DB call. Will take
    // a little bit of time to complete.

    setTimeout(() => {
        var queryResults = "A list of items";
        items = queryResults;
    }, 1000);

    return items;
}

All you end up doing is:

var itemSuccess = await undefined 

because your getData() function doesn't return a promise and, in fact, it doesn't even return the items because it returns BEFORE your timer so items (which doesn't appear to be declared properly here) has not yet received its value.

So, I would suggest you start back with the basics and go read what await really does. Make sure you understand how it interacts with a promise since it's only useful when used with a promise. And, then learn how to make your asynchronous operations return promises that are connected to when your asynchronous operations complete. Then, and only then, can you make proper use of await.

In a well organized world, you make sure that all your asynchronous operations are already returned promises and then you can async/await and .then() and .catch() to control the flow of all your asynchronous operations. In many cases, this involves learning the promise interface for whatever you're using. For example, modern versions of node.js have an entire promise interface for the file system in fs.promises.

In a few cases, there may not exist a promise interface for some library you're using and you may have to wrap the asynchronous operation in a promise to make your own promise interface. util.promisify() is built-into node.js to help you do that.

If you show the ACTUAL code, include which database you're using and the actual database call you're trying to make, then someone here can probably help you with the proper way to use the promise interface on the database. This pseudo code prevents us from helping you that specifically. Without your actual code and knowing the actual DB calls you're making in a specific DB, it's hard for us to advise much more than go use the promise interface to your database and learn how to use those promises properly. Happy to help more specifically, but we need to see you REAL code to do that.


And, lastly in Javascript if you're assigning a value to something in a higher scope from within any asynchronous callback, there's a 99.99% chance you're doing things wrong. The only place you can actually use that value is inside the callback itself or in a function that you call from there and pass the value to. So, when you do items = queryResults, where items is declared in a couple scopes above you, then alarm bells should go off that this is not going to work for you. It's not how you program with asynchronous code in Javascript, not just for style reasons, but because it doesn't work.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Perhaps it's worth demonstrating how to use `new Promise()` to wrap the `setTimeout`. I know it's obvious but if someone needs to ask this question it's probably not obvious to him – slebetman May 26 '20 at 05:12
  • @slebetman - Except the `setTimeout()` is just a dummy placeholder. I'd much rather work on the real code where we can actually show them how the code should be written which probably involves using the actual promise interfaces on the database, not manually promisifying something. We can help people so much faster and more completely if they show us the real code so that's what I try to get people to include. – jfriend00 May 26 '20 at 05:20
  • @jfriend00 and others, I could not have asked for a more thorough, comprehensive explanation. I understand why the real code would be more beneficial. Next post I shall include it. – John S. May 26 '20 at 05:48
2

Use:

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

instead of

setTimeout(() => {
}, 1000);