Search code examples
javascriptasynchronouspromisegeneratores6-promise

async issues with js generator and promises not returning result


I'm having yet another async issue where I'm lost and have no idea where or how to fix it. Forgive my bad naming.

api call to twitch api and returns an array its results.

  exports.batchPromiseWrapper = function(arr) {
  const filteredMungedDataArr = [];

  let promiseBatachArray = arr.map(vod_id => {
    var url = `https://api.twitch.tv/kraken/videos/${vod_id.id}/markers`;
    var params = { api_version: 5 };

    return axios
      .get(url, {
        params: params,
        headers: {
          "Client-ID": "xxxxxxxxxxxxxxx"
        }
      })
      .then(res => {
        return res.data;
      })
      .catch(function(error) {
        console.log(error);
      });
  });

  return Promise.all(promiseBatachArray)
    .then(markers => {
      if (markers !== null) {
        markers.map(markerObj => {
          if (markerObj.markers.game_changes !== null) {
            markerObj.markers.game_changes.forEach(gameName => {
              if (gameName.label === "Fortnite") {
                filteredMungedDataArr.push(markerObj);
              }
            });
          }
        });
        return filteredMungedDataArr;
      }
    })
    .catch(err => {
      if (err.status === 500 || err.status === 404) {
        console.log("error: ", err, err.message);
      }
    });
};

The data looks like this: [[1,2,3,4,5],[1,2,3,4,5]], generator will yield and make a promise.all call of 5 before pausing 5sec and continuing to the next batch of 5.

exports.batchFetchingGeneratorWrapper = function(generator, batchArray) {
  let evalNextValue = generator.next();

  let delay = (v, t) => {
    return new Promise(resolve => {
      setTimeout(resolve.bind(null, v), t);
    });
  };

  if (!evalNextValue.done) {
    exports.batchPromiseWrapper(evalNextValue.value).then(data => {
      let newBatchArray = batchArray;
      if (data !== undefined) {
        newBatchArray = batchArray.concat(data);
      }

      delay(5000).then(() => {
        exports.batchFetchingGeneratorWrapper(generator, newBatchArray);
      });
    });
  } else {
    console.log("yay done!", batchArray);
    return batchArray;
  }
};

I'm able to console the results in batchArray from batchFetchingGeneratorWrapper, but I unable to act on it and I know it has something to do with async and how it has yet to be resolved.

promiseDataWrapper
  .then(data => {
    return gatherData.cleanUpVODData(data);
  })
  .then(data => {
    function* batchFetching(batchArray) {
      for (let i = 0; i < batchArray.length; i++) {
        yield batchArray[i];
      }
    }

    let batchArrResult = [];
    let g = batchFetching(data);

    new Promise((resolve, reject) => {
      gatherData.batchFetchingGeneratorWrapper(g, batchArrResult);

      if (g.done) { // i dont think this works 
        console.log("batchArrResult 1: ", batchArrResult);
        resolve(batchArrResult);
      }
    }).then(result => console.log("asdfasdf", batchArrResult)); // empty array is returned
  });

Solution

  • As far as I can tell, the problem lies chiefly in batchFetchingGeneratorWrapper().

    It should be a matter of :

    • fixing delay()
    • making appropriate returns to make the recursion work
    • ensuring that the function returns Promise.

    Almost undoubtedly (syntactically) simpler with async/await but here it is with old-fashioned thens :

    exports.batchFetchingGeneratorWrapper = function(generator, batchArray) {
        let evalNextValue = generator.next();
        let delay = (t) => {
            return new Promise(resolve => {
                setTimeout(resolve, t);
            });
        };
        if (!evalNextValue.done) {
            return exports.batchPromiseWrapper(evalNextValue.value).then(data => {
                return delay(5000).then(() => {
                    return exports.batchFetchingGeneratorWrapper(generator, batchArray.concat(data || []));
                });
            });
        } else {
            console.log("yay done!", batchArray);
            return Promise.resolve(batchArray); // <<< promise wrapped to ensure that batchFetchingGeneratorWrapper() returns Promise
        }
    };
    

    And chain the batchFetchingGeneratorWrapper() call appropriately :

    promiseDataWrapper
    .then(data => gatherData.cleanUpVODData(data))
    .then(data => {
        function* batchFetching(batchArray) {
            for (let i = 0; i < batchArray.length; i++) {
                yield batchArray[i];
            }
        }
        return gatherData.batchFetchingGeneratorWrapper(batchFetching(data), []).then(batchArrResult => {
            console.log('batchArrResult: ', batchArrResult);
            return batchArrResult;
        });
    }).catch(error => {
        console.log(error);
    });