3

I'm having following code.

class DB {
  constructor(client) {
    this.client = client;
  }
}

export default function store() {
  return new Promise((resolve, reject) => {
    pg.connect(process.env.DATABASE_URL, client => {
      client.query('CREATE TABLE x(name VARCHAR(100))');
      return resolve(new DB(client));
    });
  });
}

Is there any way to move store function inside the class constructor and rewrite it using async/await?

Artem Gurzhii
  • 49
  • 2
  • 5
  • Have you already tried something? Show us your code, please. We can start from that do discuss... – Andrea Jan 30 '17 at 14:07
  • 4
    I don't think that's possible; constructors are inherently synchronous, so you can't do asynchronous operations inside the constructor (at least not properly). – Frxstrem Jan 30 '17 at 14:11
  • Agree with @Frxstrem. Usually the constructor is supposed to return your new instance. You might add the store function to your class and trigger it within your constructor though (assigning the promise to an instance property). – nils Jan 30 '17 at 15:45
  • The `async` keyword returns a promise so it's no different from using a promise only you get a nice syntax sugar with `await` – slebetman Jan 30 '17 at 15:46
  • What's the problem that you are trying to solve? – a better oliver Jan 30 '17 at 15:47
  • @zeroflagL I didn't know that [constructor function return a Promise](http://stackoverflow.com/questions/24398699/is-it-bad-practice-to-have-a-constructor-function-return-a-promise) is bad idea. So for now, I'm trying to make my code look smaller and more understandable. Now I need to find the way how to replace `Promise` with `async/await` in exported function. – Artem Gurzhii Jan 30 '17 at 19:15

2 Answers2

3

AFAIK you can't declare the constructor an async function. You can however return a Promise from the constructor. This seems to be a terrible idea, so don't use this in a real-world context.

// Define the class
class DB {
  constructor() {
    return this.store().then(client => { this.client = client; return this; });
  }

  async store() {
    const client = await new Promise((resolve) => {
      pg.connect(process.env.DATABASE_URL, resolve);
    });
    client.query('CREATE TABLE x(name VARCHAR(100))');
    return new DB(client);
  }
}

// Create an async function environment
(async function handleData() {
  const db = await new DB();
  // Do something with your DB
})();
Community
  • 1
  • 1
nils
  • 25,734
  • 5
  • 70
  • 79
  • This won't work as you expect. Constructors always return an instance. `return` in a constructor is ignored. – Jordan Running Jan 30 '17 at 16:01
  • Jup, I doubted it for a moment (as the result would be the same without the promise). Try it with an instance property instead: `class A { constructor() { this.name = 12; return Promise.resolve(this.name); } } (async () => { const a = await new A(); console.log(a); })();` – nils Jan 30 '17 at 16:08
  • Unless I completely misunderstand what's happening, this seems to work as expected. Or am I completely off base? – nils Jan 30 '17 at 16:08
  • Wow, I did not expect that at all. Sorry for jumping to conclusions. I had tested my assertion with `new (class { constructor() { return "foo"; } })()`, which returns an instance of the anonymous class rather than `"foo"`, but now I see that behavior only applies to primitives; if the `return` value is an object, the constructor will return that object, as in your code. I'm going to have to go to the spec and see what's, er, specified. P.S. I don't know who downvoted your answer, but it wasn't me. I just upvoted it. ;) – Jordan Running Jan 30 '17 at 16:24
  • [Don't return a promise from `new DB()`](http://stackoverflow.com/q/24398699/1048572). The OP's pattern with a static `store` function was totally fine and is how this should be structured. – Bergi Jan 30 '17 at 16:25
1

You cannot completely avoid the Promise constructor as you need it for promisifying the connection:

function connect(url) {
  return new Promise((resolve, reject) => {
    pg.connect(url, resolve);
  });
}

With that, you can use async/await:

export default async function store() {
  const client = await connect(process.env.DATABASE_URL);
  client.query('CREATE TABLE x(name VARCHAR(100))');
  return new DB(client);
}

If you want, you can move that function into your class, but I don't see any reason for it:

export default class DB {
  constructor(client) {
    this.client = client;
  }
  static async store() {
    const client = await connect(process.env.DATABASE_URL);
    client.query('CREATE TABLE x(name VARCHAR(100))');
    return new this(client);
  }
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375