javascriptnode.jssql-servernode-mssql

How to wait for Promise in a non-async function? (node-mssql)


I need to migrate to a MS SQL database and I'm using the node-mssql library.

Problem: the mssql library returns Promises and they don't block the execution of my code.

But my code expects the code to block and I cannot effort refactoring the whole project so I'm looking for a solution to wait for a Promise until it's done and to return its result.

All answers I found do not solve the problem.

When my code queries the database then it must return a result, not a Promise object that is resolved somewhen later. My code needs to block and wait for the Promise to finish (succeed or fail) and continue with its result.

I see that many others do have the same requirement. Why is it so hard to implement an await-like statement in a non-async function? These fancy Promise bullsh*t callbacks are driving you crazy.

An acceptable alternative is to switch to another MS SQL library that acts "like normal".

Here's an example:

// Emulate the node-mssql connect method
// Similar to: const sql = require('mssql')
const sql = {
  connect: (config) => new Promise(resolve => {
    setTimeout(() => resolve({query: (sql) => sql}), 2000)
  })
}

// Existing project structure
class Foo {
  db; // database connection
  
  constructor() {
    this.db_connect();
  }
  
  db_connect() {
    this.db = sql.connect(); // Promise, not connection object
    // must not return until done!
  }
  
  db_query(sql) {
    return this.db.query(sql); // Promise, not result object
    // must not return until done!
  }
}

const foo = new Foo();
console.log(foo.db_query(`SELECT * FROM bar WHERE spam='eggs'`));

// Uncaught TypeError: this.db.query is not a function

Solution

  • The constructor cannot be async; however, the other methods can. We'll make those async and we'll instantiate the object and invoke it's methods from within an async main function:

    const sql = {
      connect: (config) => new Promise(resolve => {
        setTimeout(() => resolve({query: (sql) => sql}), 2000)
      })
    }
    
    // Existing project structure
    class Foo {
      db;
    
      constructor() {
        this.db = null;
      }
    
      async db_connect() {
        this.db = await sql.connect();
      }
    
      async db_query(sql) {
        return await this.db.query(sql);
      }
    }
    
    const main = async () => {
      const foo = new Foo();
      await foo.db_connect()
      const out = await foo.db_query(`SELECT * FROM bar WHERE spam='eggs'`)
      console.log(out);
    };
    
    main();