Javascript/Typescript Export Default Const as value from async function call

Tags: , , , ,



i’ve done a bunch of reading but haven’t found a working solution

the closest i’ve seen is here: Export the result of async function in React

please keep in mind that I want to export an object, and that object is the result of an asynchronous function, NOT export the async function definition itself

here’s my use case and implementation so far:

  1. we have a file called config.ts

  2. traditionally config.ts has contained an object with relevant runtime configurations as the default export

    this lets us simply import config from '../config' or whatever

  3. our config and secret management has gotten more complex so that it needs to make various calls to various secret repositories (aws, azure, etc)

  4. i’ve refactored config.ts to now look something like this:

export const basicValues = {
    color1: 'red'
}

async function buildConfig(){
    const valuesOut = {...basicValues}
    valuesOut.color2 = await getSecret('color2');
    valuesOut.color3 = await getSecret('color3');

    return valuesOut;
}

export default buildConfig()

where getSecret is some function that makes an arbitrary async call

i’m exporting basicValues above because i have some config settings in there that are necessary to make the calls inside getSecret.

by exporting basicValues like this i can get the values out with a simple const basicConfig = require('../config').basicValues. This way we can continue to manage all the fun config stuff in a clean, centralized, tested file but still make the values available for use very early and avoid cyclical dependencies

all together, this FEELS like it should work

i’ve tried lots of other patterns but this feels most natural and intuitive to read

here’s the bad part:

  1. import config from '../config' yields undefined, with export default buildConfig()
  2. changing the export to be simply export default basicValuesgives us our config object as expected (but without the async values populated, obviously)

what exactly am I doing wrong here?

happy to provide more deets as necessary

thanks in advance

Answer

please keep in mind that I want to export an object, and that object is the result of an asynchronous function, NOT export the async function definition itself

This is not possible. Since the value is retrieved asynchronously, all modules that consume the value must wait for the asynchronous action to complete first – this will require exporting a Promise that resolves to the value you want.

In new versions of Node, you could import the Promise and use top-level await to wait for it to be populated:

import buildConfigProm from '../config';
const config = await buildConfigProm;

If you aren’t in Node, top-level await isn’t supported. You could call .then on the Promise everywhere it’s imported:

buildConfigProm.then((config) => {
  // put all the module's code in here
});

If you don’t like that, the only real alternative is to use dependency injection. Have your modules export functions that take the config as a parameter, eg:

// useConfig.ts
export default (config: Config) => {
  console.log('color2', config.color2);
};

This way, the only thing that has to be asynchronous is the entry point, which waits for the Promise to resolve, then calls the needed modules with it:

// index.ts

import buildConfigProm from './config';
import useConfig from './useConfig';
buildConfigProm
  .then((config) => {
    useConfig(config);
  })
  .catch(handleErrors);


Source: stackoverflow