syarul
syarul

Reputation: 2189

Resolve promises in object tree

  1. How do I resolve foo3

  2. How can I resolve the object tree without async/await with only plain Promise (without depending on regenerator-runtime)

const p = v => {
  return new Promise(resolve => 
    setTimeout(resolve.bind(null, v), 2000)
  )
}

const tree = {
  element: p('foo'),
  children: [
    p({
      element: 'bar',
      children: null
    }),
    p({
      element: 'bar2',
      children: [
        {
          element: p('foo3'),
          children: null
        }
      ]
    })
  ]
}

const fullfill = async vtree => {
  if(vtree instanceof Promise) {
    await vtree
  } else if(Array.isArray(vtree)) {
    await Promise.all(vtree.map(fullfill))
  } else if(typeof vtree !== 'object') {
    return vtree
  } else {
    let { element, children = [] } = vtree
    if(element instanceof Promise) {
      element = await element
    }
    if(children.length) {
      children = await Promise.all(children.map(fullfill))
    }
    return {
      element,
      children
    }
  }
  return vtree
}

fullfill(tree).then(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>

Upvotes: 1

Views: 175

Answers (2)

Bergi
Bergi

Reputation: 664185

There's hardly a reason to use async/await if all you're doing is to return the result anyway. You'd only use it for the tree node (element+children) case, but there you can easily do the transformation after the necessary Promise.all with then as well:

function treeAll(vtree) {
  if (typeof vtree !== 'object' || vtree == null) {
    return Promise.resolve(vtree);
  } else if (vtree instanceof Promise) {
    return vtree.then(treeAll);
  } else if (Array.isArray(vtree)) {
    return Promise.all(vtree.map(treeAll));
  } else {
    return Promise.all([
      vtree.element,
      treeAll(vtree.children)
    ]).then(([element, children]) => ({
      element,
      children
    }));
  }
}

Upvotes: 1

Karan
Karan

Reputation: 12619

Problem with your code was your children value of tree object was processed by first if (vtree instanceof Promise) {. And their child never processed.

To properly process child I removed assigned assign awaited object back to vtree inside first if (Change 1) & add vtree = await fullfill(vtree) next to it (Change 2).

Use children && children.length so if children is null then it won't throw exception. (Change 3)

Test it below.

const p = v => {
  return new Promise(resolve =>
    setTimeout(resolve.bind(null, v), 2000)
  )
}

const tree = {
  element: p('foo'),
  children: [
    p({
      element: 'bar',
      children: null
    }),
    p({
      element: 'bar2',
      children: [{
        element: p('foo3'),
        children: null
      }]
    })
  ]
}

const fullfill = async vtree => {

  if (vtree instanceof Promise) {
    // Chanage 1
    // assign awaited object back to vtree
    vtree = await vtree;
    // Chanage 2
    // Call fullfill and pass vtree
    vtree = await fullfill(vtree);
  } else if (Array.isArray(vtree)) {
    await Promise.all(vtree.map(fullfill))
  } else if (typeof vtree !== 'object') {
    return vtree
  } else {
    let {
      element,
      children = []
    } = vtree
    if (element instanceof Promise) {
      element = await element
    }
    // Chanage 3
    // use children && children.length so if children is null then it won't throw exception.
    if (children && children.length) {
      children = await Promise.all(children.map(fullfill));
    }
    return {
      element,
      children
    }
  }
  return vtree
}

fullfill(tree).then(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>


Edit without await. To remove await we need to return Promise. Please review first code snippet and then check equivalent version in second snippet which return Promise instead of await.

Test it below.

const p = v => {
  return new Promise(resolve =>
    setTimeout(resolve.bind(null, v), 2000)
  )
}

const tree = {
  element: p('foo'),
  children: [
    p({
      element: 'bar',
      children: null
    }),
    p({
      element: 'bar2',
      children: [{
        element: p('foo3'),
        children: null
      }]
    })
  ]
}

const fullfill = async vtree => {

  if (vtree instanceof Promise) {
    // Chanage 1
    // assign awaited object back to vtree
    return vtree.then(r => fullfill(r))
    //vtree = await vtree;
    //vtree = await fullfill(vtree);
  }

  // Chanage 2
  // update else if condition to if here.
  else if (Array.isArray(vtree)) {
    return Promise.all(vtree.map(fullfill))
  } else if (typeof vtree !== 'object') {
    return vtree
  } else {
    let {
      element,
      children = []
    } = vtree
    if (element instanceof Promise) {
      // element = await element
      return element.then(e => {
        if (children && children.length) {
          return Promise.all(children.map(fullfill)).then(c => ({
            element: e,
            children: c
          }));
        };
        return {
          element: e,
          children: children
        };
      });
    }
    // Chanage 3
    // use children && children.length so if children is null then it won't throw exception.
    else if (children && children.length) {
      return Promise.all(children.map(fullfill)).then(c => ({
        element: element,
        children: c
      }));
    }
  }
  return vtree
}

fullfill(tree).then(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>

Upvotes: 1

Related Questions