Reputation: 1399
I'm having an issue getting this logic to work, and I don't even know if it's possible or if I'm correctly using promises here.
I have this first function that takes in a list of ID's that are used to make a request to pull in specific async data. I can only pull in 1000 at a time, so if the list contains more than 1000, I have to recurse through it.
What I'm looking for is this - For each option that you need to recurse, call the recursive function, await the results and then push them to a list. When all the data is obtained, respond with the data.
async function getData () {
....
// Inside condition that determines it needs to recurse
let _data = await _getOptions(id, data, 0);
dataList.push(_data);
...
// Eventually, return the data
res.json(dataList);
}
This function recurses through the list and is supposed to resolve once all the data is pulled in.
let rData = [];
function _getOptions(id, data, offset) {
return new Promise((resolve, reject) => {
if (data.length < 1000) {
return resolve(rData);
}
let fields = { fields: `id, key_value, label, condition_value`, offset: offset, limit: 1000 };
options.getOptionsWithFilter(id, fields, (err, data) => {
rData.push(data);
_getOptions(id, data, offset + 1000);
});
});
}
It doesn't seem that this is working because of an error thrown in the console, that would suggest to me it's returning undefined
. I would guess the issue is that I'm using promises wrong here. But I've been struggling to come up with another solution. I need all the data to be returned in the _data
variable before I can move on.
Upvotes: 0
Views: 135
Reputation: 350272
Your promise constructor callback should always result in a call to resolve
(or reject
), but when you go on to make the recursive call, you don't ever call resolve
on that particular promise. So call it, like this:
resolve(_getOptions(id, data, offset + 1000));
I also think your way of combining the chunks of result data is wrong: you would push arrays as nested arrays into the final array. I would expect you would need to concatenate the chunks into a 1 dimensional array.
There is an issue with the use of the data
variable: you use it for something that is passed to the _getOptions
function (not sure what that initially is), but also for the provided from getOptionsWithFilter
. I think that latter data is in fact the only data
you are dealing with. So then you don't need to pass it to _getOptions
.
It would also be advisable to avoid the use of a global rData
variable. As you only set it to the empty array once, you risk to not have cleared it for any next request you want to make.
Instead let the final promise yield the final chunk of data and then prepend the previous chunks to it:
function _getOptions(id, offset = 0) { // No data argument!
return new Promise((resolve, reject) => {
let fields = { fields: `id, key_value, label, condition_value`,
offset: offset, limit: 1000 };
options.getOptionsWithFilter(id, fields, (err, data) => {
if (data.length < 1000) { // Perform the check when you have the data
return resolve(data); // just this chunk
}
resolve(_getOptions(id, offset + 1000) // Don't pass data
// prepend this data to the data that the recursive
// promise will provide
.then(rData => data.concat(rData))
);
});
});
}
You call it like this:
_data = await _getOptions(id);
You could also choose to perform the asynchronous loop ("recursive call") inside one Promise constructor callback:
function _getOptions(id) { // No data argument!
return new Promise((resolve, reject) => {
let rData = [];
(function loop(offset) {
let fields = { fields: `id, key_value, label, condition_value`,
offset: offset, limit: 1000 };
options.getOptionsWithFilter(id, fields, (err, data) => {
rData = rData.concat(data);
if (data.length < 1000) { // Perform the check when you have the data
return resolve(rData); // All retrieved data
}
loop(offset + 1000); // Collect more
});
})(0); // Call immediately
});
}
Upvotes: 5