Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The proper ways to stack optioned promises

Tags:

What would be the proper or the best way to collect all data from DB with promises, but with using native Node promises.

The goal is only to present what is selected:

const allPromises = [];
const selected = {
  sectionA: true,
  sectionB: false,
  sectionCIds: [ 1, 2, 4 ],
};

if (selected.sectionA) {
  allPromises.push(getSectionADataFromDbPromise());
}
if (selected.sectionB) {
  allPromises.push(getSectionBDataFromDbPromise());
}
if (selected.sectionCIds.length > 0) {
  allPromises.push(selected.sectionCIds
    .map(getSectionCDataFromDbPromise)
  );
}
Promise.all(allPromises)
  .then((allResults) => {
    if (selected.sectionA) {
      dataA = allResults[0];
    }
    if (selected.sectionA) {
      dataB = allResults[1];
    }
    if (selected.sectionC) {
      dataC = allResults[2]; // <-- this will not work if B is not selected
    }

    // ... same logic to build report: return Promise.all()...
  });

Possible solutions:

  • Track index for each data selected (eg. index of C will be 1)
  • Object Map
  • Add else { allPromises.push(Promise.resolve(null)) } to every if

Is there maybe an easier or one of this will be the proper way?

like image 221
Vladimir Vukanac Avatar asked Nov 24 '17 09:11

Vladimir Vukanac


2 Answers

Don't use push on the arrays conditionally, but always put the same value at the same index. Even if the value is nothing - Promise.all will handle that just fine.

const selected = {
  sectionA: true,
  sectionB: false,
  sectionCIds: [ 1, 2, 4 ],
};
Promise.all([
  selected.sectionA ? getSectionADataFromDbPromise() : null,
  selected.sectionB ? getSectionBDataFromDbPromise() : null,
  Promise.all(selected.sectionCIds.map(getSectionCDataFromDbPromise))
]).then(([dataA, dataB, dataC]) => {
  if (selected.sectionA) {
    // use dataA
  }
  if (selected.sectionA) {
    // use dataB
  }
  if (dataC.length) { // same as selected.selectionCIds.length
    // use dataC
  }
});
like image 176
Bergi Avatar answered Oct 06 '22 01:10

Bergi


What do you think about this ? It's bigger, it's heavier, it's more difficult, but it's all automatized and fully evolutive. Wanna handle a new parameter ? A parameter have data now ? Change the map only.


I create a map that would contains everything we need to use a loop. The state of the data (activated or not), the function to call to get the data and so on.

const mapSelected = {
  sectionA: {
    state: true,

    func: getSectionADataFromDbPromise,
  },

  sectionB: {
    state: false,

    func: getSectionBDataFromDbPromise,
  },

  sectionC: {
    state: true,

    func: getSectionCDataFromDbPromise,

    data: [
      1,
      2,
      4,
    ],
  },
};

Then we create the promise array using the map we has created. Handling the case with data and without data.

const promises = Object.values(mapSelected).reduce((tmp, {
  state,
  func,
  data,
}) => {
  if (!state) return tmp;

  if (data && data.length) {
    return [
      ...tmp,

      ...data.map(x => func.call(this, x)),
    ];
  }

  return [
    ...tmp,

    func.call(this),
  ];
});

Then we create arrays from the promise return for each key on the map. You can change how I present the data, I didn't knew what you really wanted there.

Promise.all(promises)
  .then((allResults) => {
    let i = 0;

    const [
      dataA,
      dataB,
      dataC,
    ] = Object.values(mapSelected).reduce((tmp, {
      state,
      data,
    }, xi) => {
      if (!state) return tmp;

      if (data && data.length) {
        data.forEach(x => (tmp[xi].push(allPromises[i++])));

        return tmp;
      }

      tmp[xi].push(allPromises[i++]);

      return tmp;
    }, Object.values(mapSelected).map(() => []));
  });


@EDIT

I just did a snippet about the code I've made, run it

function a() {
  return 1;
}

const mapSelected = {
  sectionA: {
    state: true,

    func: a,
  },

  sectionB: {
    state: false,

    func: a,
  },

  sectionC: {
    state: true,

    func: a,

    data: [
      1,
      2,
      4,
    ],
  },
};

const allPromises = [
  0,
  1,
  2,
  3,
  4,
];

let i = 0;

const [
  dataA,
  dataB,
  dataC,
] = Object.values(mapSelected).reduce((tmp, {
  state,
  data,
}, xi) => {
  if (!state) return tmp;

  if (data && data.length) {
    data.forEach(x => (tmp[xi].push(allPromises[i++])));

    return tmp;
  }

  tmp[xi].push(allPromises[i++]);

  return tmp;
}, Object.values(mapSelected).map(() => []));

console.log(dataA);
console.log(dataB);
console.log(dataC);
like image 28
Orelsanpls Avatar answered Oct 06 '22 00:10

Orelsanpls