Reputation: 755
I'm trying to create a recursive Promise
call inside a reduce
in JS. The goal for my system here is to make n big calls for each item in the array that reduce
plays with, then, if, inside that reduce
that big call decides it needs n smaller calls before it gets back to the big ones, then the reduce
shouldn't skip to the next item, just wait for it to finish.
My current code is:
function download_preliminary_files_1() {
let demo_schema_download = new Base_Ajax_Requester(
localized_data.ajax_url,
{
'ajax_action': 'download_demo_file_schema',
'ajax_nonce': localized_data.ajax_nonce,
'backend': {
'data_handle': 'download_demo_file_schema_data',
'data':
{
'demo_handle' : 'demo-2',
}
}
}
);
let import_files_download = demo_schema_download.call().then(function() {
fake_data.steps_to_import.reduce(function(previous_promise, next_step_identifier) {
return previous_promise.then(function() {
let file_download = download_demo_file({
'demo_handle' : 'demo-2',
'step' : next_step_identifier
}).call();
file_download.then(function(response) {
/**
* Here, if I detect that response.remaining_number_of_files > 0, I should start
* a chain that keeps calling `download_demo_file` with new parameters.
*
* Then, when this chain is done, resume the normal reduce behavior.
*/
});
});
}, Promise.resolve())
}).catch(function(error) {
console.log( 'Got this error:' + error);
});
return import_files_download;
}
Where Base_Ajax_Requester
is a helper class that handles AJAX requests and returns a Promise
when it's done so code could be written around it.
My fake_data
is:
let fake_data = {
'demo_handle' : 'demo-2',
'steps_to_import' : [ 'elementor-hf','post', 'nav_menu' ]
}
As you can see fake_data.steps_to_import.reduce(..
will go through these 3 values, for each of them call download_demo_file
, wait for it to finish, then proceed to the next. We could say that I'd like to, between elementor-hf
and post
to have n smaller calls.
The initial call to download_demo_file
is seen up there, here's what will always come back from the back-end:
{
'message' : '...',
'file_number' : 1, //Which -n.xml file has been downloaded where n is this number.
'remaining_number_of_files' : 1 //Calculates, based on what file was downloaded how many files are left. The system knows internally how many files it has to download.
}
The retry call, again, with download_demo_file
would look like:
{
'demo_handle' : 'demo-2',
'step' : next_step_identifier,
'file_counter' : 2 //Dynamic. This will signal that it needs to now download file-2.xml.
}
...and so on, until the back-end sends remaining_number_of_files : 0
, then it all stops because there are no more files to download and it can skip to the next big call.
How can I achieve this?
Upvotes: 1
Views: 58
Reputation: 370789
Inside each reduce
callback, I'd make a function that calls download_demo_file
(with a changing file_counter
), and after that Promise resolves, recursively returns a call of itself if remaining_number_of_files > 0
. This will mean that getProm()
will continually call itself until the remaining_number_of_files > 0
condition is not fulfilled anymore, and that only after that will the whole Promise for that particular reduce
iteration resolve.
let import_files_download = demo_schema_download.call().then(function() {
fake_data.steps_to_import.reduce(function(previous_promise, step) {
let file_counter = 1;
return previous_promise.then(function() {
const getProm = () => download_demo_file({
demo_handle: 'demo-2',
step,
file_counter
}).call()
.then((response) => {
file_counter++;
return response.remaining_number_of_files > 0
? getProm()
: response
});
return getProm();
});
}, Promise.resolve())
}).catch(function(error) {
console.log('Got this error:' + error);
});
The code would probably be a lot easier to read and understand with async
/await
, though:
await demo_schema_download.call();
const import_files_download = [];
for (const step of fake_data.steps_to_import) {
let response;
let file_counter = 1;
do {
response = await download_demo_file({
demo_handle: 'demo-2',
step,
file_counter
}).call();
file_counter++;
} while (response.remaining_number_of_files > 0);
import_files_download.push(response); // is this needed?
}
return import_files_download; // is this needed?
Catch in the consumer of the async function.
Upvotes: 1