amir hosein ahmadi amir hosein ahmadi - 56 minutes ago 2
Javascript Question

how to refactor waterfall .then()

this code here load data using actions and would be series , but its gonna be hard to edit this code add another api load , and the syntax is not clear .



this.props.loadNutMatrixes({perPage:'all'}).then(()=>{
this.props.loadIngredients().then(()=>{
this.props.getBadge().then(()=>{
this.props.loadNutInfoItems({perPage:'all'}).then(()=>{
this.props.getItemSize().then(()=>{
this.props.getSingleMenuCategory(this.props.category_uid).then(()=>{
this.props.loadAllStores(({per_page:'all'})).then(()=>{
if (this.props.selectedMenuItem ){
initialize("addNewMenuItem", {
...this.props.selectedMenuItem
})
}
})
})
})
})
})
})
})




Answer

You can convert this to a vertical structure by chaining the promises instead of nesting:

this.props.loadNutMatrixes({perPage:'all'})
  .then(() => this.props.loadIngredients())
  .then(() => this.props.getBadge())
  .then(() => this.props.loadNutInfoItems({perPage:'all'}))
  .then(() => this.props.getItemSize())
  .then(() => this.props.getSingleMenuCategory(this.props.category_uid))
  .then(() => this.props.loadAllStores(({per_page:'all'})))
  .then(() => {
    if (this.props.selectedMenuItem) {
      initialize("addNewMenuItem", {
        ...this.props.selectedMenuItem
      })
    }
  });

A possible improvement could be to wrap all promise-creating functions that receive arguments into functions without arguments and pass those as props instead:

loadAllNutMatrixes() {
  return this.loadNutMatrixes({ perPage: 'all' });
}

loadAllNutInfoItems() {
  return this.loadNutInfoItems({ perPage: 'all' });
}

getSingleMenuCategoryFromId() {
  return this.getSingleMenuCategory(this.category_uid);
}

loadEveryStory() {
  return this.loadAllStores({ perPage: 'all' });
}

Then you could refactor the last step into its own method:

onChainFinished() {
  if (this.props.selectedMenuItem) {
    initialize("addNewMenuItem", {
      ...this.props.selectedMenuItem
    })
  }
}

And combine the two with some destructuring to achieve a cleaner chain:

const { props } = this;
props.loadAllNutMatrixes()
  .then(props.loadIngredients)
  .then(props.getBadge)
  .then(props.loadAllNutInfoItems)
  .then(props.getItemSize)
  .then(props.getSingleMenuCategoryFromId)
  .then(props.loadEveryStore)
  .then(this.onChainFinished);

EDIT based on your comment

using something like promise.all but in series way !

There is no native method to chain Promises but you can build a helper method suitable for your use case to do this. Here's a general example:

// `cp` is a function that creates a promise and 
// `args` is an array of arguments to pass into `cp`
chainPromises([
  { cp: this.props.loadNutMatrixes, args: [{perPage:'all'}] },
  { cp: this.props.loadIngredients },
  { cp: this.props.getBadge },
  { cp: this.props.loadNutInfoItems, args: [{perPage:'all'}] },
  { cp: this.props.getItemSize },
  { cp: this.props.getSingleMenuCategory, args: [this.props.category_uid] },
  { cp: this.props.loadAllStores, args: [{per_page:'all'}] }
]).then(() => {
  if (this.props.selectedMenuItem) {
    initialize("addNewMenuItem", {
      ...this.props.selectedMenuItem
    })
  }
});

function chainPromises(promises) {
  return promises.reduce(
    (chain, { cp, args = [] }) => {
      // append the promise creating function to the chain
      return chain.then(() => cp(...args));
    }, Promise.resolve() // start the promise chain from a resolved promise
  );
}

If you use the same approach as above to refactor methods with arguments, it would clean this code up as well:

const { props } = this;
chainPropsPromises([
  props.loadAllNutMatrixes,
  props.loadIngredients,
  props.getBadge,
  props.loadAllNutInfoItems,
  props.getItemSize,
  props.getSingleMenuCategoryFromId,
  props.loadEveryStory
])
.then(this.onChainFinished);

function chainPropsPromises(promises) {
  return promises.reduce(
    (chain, propsFunc) => (
      chain.then(() => propsFunc());
    ), Promise.resolve()
  );
}