0

consider this,

let value = "";
value = DATABASE_CALL();

module.exports = value;

When I require the above module in an another module and try to access the variable 'value', it is an empty string. How can I make the module.exports wait until the above DB call is completed and the variable is assigned a value?

Soorya J
  • 145
  • 6
  • 1
    Export a function that calls the database? – evolutionxbox Jul 11 '22 at 14:07
  • I assume `value = DATABASE_CALL(0;` is a stand-in for asynchronous code. If it were really synchronous like that, you'd just use it as the initializer value on `value`. – T.J. Crowder Jul 11 '22 at 14:11
  • @evolutionxbox Could you please elaborate your point? it will be useful for me. Thanks in advance. – Soorya J Jul 11 '22 at 14:16
  • 2
    You cannot make the module "wait". Instead of exporting the value, export a function (an *asynchronous* function) that can be used by a module that imports this module to get the value. – Pointy Jul 11 '22 at 14:17
  • I agree with you @T.J.Crowder, the DB call is an asynchronous piece of code, though you put `let value = DB_CALL();` then export it, it has value `undefined` in the other module. How can I handle this situation? Thanks in advance – Soorya J Jul 11 '22 at 14:19
  • `module.exports = DATABASE_CALL` – vanowm Jul 11 '22 at 14:21
  • Thanks @Pointy, I understood it, its a good one, I will try it, is there any other solutions for this, any other ways of achieving this? Thanks in advance – Soorya J Jul 11 '22 at 14:21
  • Thanks @vanowm, just to confirm, is this the official way of handling the situation ? – Soorya J Jul 11 '22 at 14:23
  • @Pointy - Well, you can now with ESM and top-level `await`. But the OP is using CommonJS... – T.J. Crowder Jul 11 '22 at 14:24
  • @SooryaJ - There are no official ways, and note that what vanowm has shown is completely different to what your original code was trying to do. It's just exporting the function, not the value. – T.J. Crowder Jul 11 '22 at 14:24
  • @T.J.Crowder right, that's clear. However wouldn't the exporting module need to export the Promise? (Well I guess that's what it'd do if `DATABASE_CALL()` was `async` or returned a Promise explicitly.) – Pointy Jul 11 '22 at 14:46
  • @Pointy - Not with top-level `await`, its purpose is to hold up module resolution until the promise settles (making module resolution fail if the promise rejects, though of course you could handle the rejection instead if you have a reasonable fallback value or similar). The code importing just sees the exported binding and doesn't know that the module waited for a promise before supplying it. – T.J. Crowder Jul 11 '22 at 14:58

1 Answers1

3

I assume value = DATABASE_CALL(); is a stand-in for asynchronous code. (If it were really synchronous as shown there, you'd just use it as the initializer value on value.)

You have a few options for exporting a value that's only available asynchronously:

  1. Using ESM instead of CommonJS and using top-level await to prevent he module from finishing loading until the database operation is complete.

    export default await DATABASE_CALL();
    

    ...where DATABASE_CALL returns a promise. (If it doesn't already, you can wrap it.)

    Code using it won't have to await it, but the module won't finish loading until the DB op is complete, and won't load at all if the DB op fails.

    This is really only appropriate if the module cannot be used at all without that value.

  2. Exporting a promise of the value (whether you switch to ESM or keep using CommonJS):

    module.exports = DATABASE_CALL();
    

    ...where (again) DATABASE_CALL returns a promise.

    Code using it would have to await the promise (or use explicit .then/.catch).

  3. Using ESM instead of CommonJS and using a flag value to indicate "not ready yet" (null and undefined are useful for that) and updating the value when the database call is complete:

    export let value = null;
    // ...later when DATABASE_CALL is complete
    value = /*...the value from the DB...*/;
    

    ESM exports are live bindings, meaning that if the source module updates the variable, imports in other modules see the updated value.

    Note: You can only do this with named exports (value in the example), not default exports.

    Of course, that means the code using it has to allow for the possibility it's not loaded yet.

  4. Exporting a function that provides the value, and that handles telling the caller the value isn't available yet (implementation left to the reader).

There are probably other choices.

In cases where the module is worthless without the asynchronous information, I usually use #1. If not, I usually use #2.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875