Search code examples
javascriptasynchronousdata-structuresdeferredlazy-initialization

How to resolve all async deferred nested values of a lazy initializable data structure?


I'm looking for vocabulary or for a library that supports the following behaviour:

Imagine a Javascript object like the following one:

const foo = {
  id: 1,
  name: 'Some String value',
  supplier: async () => {
    return 'Some supplier name'
  },
  nested: async () => {
    return [
      {
        id: 2,
        name: async () => {
          return 'this is a name'
        }
      }
    ]
  }
}

It is composed by native types (numbers, strings...) and by functions.

I'd like this object being transformed to the following one:

const resolved = {
  id: 1,
  name: 'Some string value',
  supplier: 'Some supplier name',
  nested: [
    {
      id: 2,
      name: 'this is a name'
    }
  ]
}

As you see the transformed object does not have functions anymore but only native values.

If you are familiar with GraphQL resolvers, it might ring a bell to you.

I know I can write my own implementation of the behaviour but I'm sure this is something that already exists somewhere.

Do you have some keywords to share?


Solution

  • I doubt there's a library that does exactly this, unless you're actually thinking of the graphql-js execute method.

    But it's easy enough to implement yourself:

    async function initialised(value) {
      if (typeof value == 'function') return initialised(await value());
      if (typeof value != 'object' || !value) return value;
      if (Array.isArray(value)) return Promise.all(value.map(initialised));
      return Object.fromEntries(await Promise.all(Object.entries(value).map(([k, v]) =>
        initialised(v).then(r => [k, r])
      )));
    }
    

    async function initialised(value) {
      if (typeof value == 'function') return initialised(await value());
      if (typeof value != 'object' || !value) return value;
      if (Array.isArray(value)) return Promise.all(value.map(initialised));
      return Object.fromEntries(await Promise.all(Object.entries(value).map(([k, v]) =>
        initialised(v).then(r => [k, r])
      )));
    }
    
    const foo = {
      id: 1,
      name: 'Some String value',
      supplier: async () => {
        return 'Some supplier name'
      },
      nested: async () => {
        return [
          {
            id: 2,
            name: async () => {
              return 'this is a name'
            }
          }
        ]
      }
    };
    
    initialised(foo).then(resolved => {
      console.log(resolved);
    })