0

Apologies if I am missing something trivial. What I want to do is to call database queries synchronously. I need to make sure the database inserts are completed before moving to the next step. I also do not want to nest in the callback. I am trying to use promises but it doesn't seem to work as I expected. This is my code:

async init()
{
        await this.initWalletTable();
}


async initWalletTable()
{
   console.log("Creating wallet table");
   var self = this;
   walletParams.wallets.forEach(function(wallet) {

   (async() => {
         await self.insertWallet(wallet); 
         console.log("inserted " + wallet.multisig_wallet_name);
         })();
      });
 }


 insertWallet(wallet)
 {
      console.log("Inserting wallet " + wallet.multisig_wallet_name);
      return new Promise((resolve, reject) => {

            pool.query(`INSERT INTO ${schema}wallet \
                        (name, wallet_name_1, ) \
                        VALUES ($1, $2) \
                        ON CONFLICT (name) DO NOTHING`, [wallet.multisig_wallet_name, wallet.wallet_name1])
            .then(dbres => {
                return resolve(true);
            })
            .catch(e => {
                console.error(e.stack);
               return  resolve(true);
            })

        });
    }
3
  • 1
    You can't turn asynchronous database calls into synchronous operations in Javascript. Can't be done. If what you really want to do is "sequence" several operations so the second happens after the first is done, that can be done easily. Please clarify. Commented Jul 31, 2019 at 2:30
  • 1
    FYI, .forEach() does not wait for await. Use a regular for loop if you want the loop to pause for await. And, it appears you're misunderstanding await too because making an async IIFE with a single await in it doesn't really pause anything. Commented Jul 31, 2019 at 2:31
  • Thank you @jfriend00 Yes, I want the DB inserts to complete (the forEach) loop and then go to the next statement. Did not know forEach does not wait for await. Thank you. I am using node-postgres and it seems I can call queries with await but even then I am having problems. What is the best way to achieve this? Commented Jul 31, 2019 at 2:38

2 Answers 2

1

Lots of things wrong with your existing code:

  1. You can't turn asynchronous database calls into synchronous operations in Javascript. Can't be done. If what you really want to do is "sequence" several operations so the second happens after the first is done, that can be done easily.

  2. A .forEach() loop does not wait for await. You can use a regular for loop instead.

  3. An async IIFE with just a single await in it, does not really do anything useful. The IIFE still returns immediately (not waiting for the await).

  4. Keep in mind that an async function returns a promise immediately as soon as it hits the first await. It doesn't wait for the async operations to finish before it returns.

  5. You never want to wrap an existing promise in a manually created new Promise(). Instead you just return the promise you already have.

  6. You need an error handling strategy in this code. Right now, it looks like your code tries to just ignore errors. Is that really the right strategy?

You can do this instead:

init() {
    return this.initWalletTable().then(() => {
        console.log("all wallets inserted");
    });
}


async initWalletTable() {
   console.log("Creating wallet table");
   for (let wallet of walletParams.wallets) {
       try {
            await this.insertWallet(wallet);
            console.log("inserted " + wallet.multisig_wallet_name);
       } catch(e) {
           // decide what you're doing if you get an error inserting
            console.log("error inserting " + wallet.multisig_wallet_name);
           throw e;
       }
   }
 }


insertWallet(wallet) {
    return pool.query(`INSERT INTO ${schema}wallet \
                        (name, wallet_name_1, ) \
                        VALUES ($1, $2) \
                        ON CONFLICT (name) DO NOTHING`, [wallet.multisig_wallet_name, wallet.wallet_name1])
        });
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks so much @jfriend00. This works. Yes, I need to have error handling. There's no error handling right now. Can I call the this.initWallet().then(() => {... as await this.initWallet()? Would that await not work there in init()? Thank you.
@madu - Note that doing some sort of asynchronous initialization in a constructor is a somewhat troublesome issue to design for. The caller needs to know when it's safe to use the new object (when all the initialization is done). You can see a discussion of some of the various options here: Asynchronous Operations in Constructor.
@madu - If your init() method is async, you can use await. I don't generally use await when I only have one asynchronous operation, but that's a personal style. Also, make sure you communicate back completion and/or errors properly.
1

I think the solution I laid out below solves your problem.

What I want to do is to call database queries synchronously. I need to make sure the database inserts are completed before moving to the next step. I also do not want to nest in the callback.

I'm not sure what you are asking. I think I know what you mean, and I tried to solve your problem, but if I am not understanding your intent, please let me know. A couple points for clarification:

  • Promises are designed to make asynchronous code easier to maintain and understand (rather than nesting functions).
  • async / await builds on top of that by making it easy for you to write synchronous looking code
  • this asynchronous looking code still uses promises under the hood, which use callbacks under the hood.
  • You want your methods to be asynchronous, especially since this is database io.

You are asking to have your asynchronous code to run in series, not all at the same time.

Changes Made

  • In insertWallet method, because pool.query() already returns a promise, you do not need to wrap it in an explicit promise.
  • I'm not sure why you wrapped an async anonymous function in an immediately-invoked-function-expression inside initWalletTable(), but I think you will be fine if you remove that block.

Code

class Wallet {
  constructor(walletParams) {
    this.walletParams = walletParams;
    this.initialized = this.init();
  }

  async init() {
    return await this.initWalletTable();
  }

  async initWalletTable() {
    console.log("Creating wallet table");
    this.walletParams.wallets.forEach((wallet) => {
      await this.insertWallet(wallet);
      console.log("inserted " + wallet.multisig_wallet_name);
    });
  }

  async insertWallet(wallet) {
    console.log("Inserting wallet " + wallet.multisig_wallet_name);
    return pool.query(`INSERT INTO ${schema}wallet \
                        (name, wallet_name_1, ) \
                        VALUES ($1, $2) \
                        ON CONFLICT (name) DO NOTHING`, [wallet.multisig_wallet_name, wallet.wallet_name1])
      .catch(e => {
        console.error(e.stack);
        throw e; // re-raise error
      });
  }
}

2 Comments

The .forEach() loop will generate an error since you're attempting to use await inside a non-async callback. Even if you mark it async, the loop won't pause for the await so you'll be doing all the inserts in parallel. Also, there's no reason for insertWallet() to be tagged async.
Thank you @FelizeNaveedad. Yes, I have completely misusndertood the mechanism of promises. I will try this one out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.