Я пытаюсь создать рекурсивный вызов Promise
внутри reduce
в JS. Цель моей системы здесь состоит в том, чтобы сделать n больших вызовов для каждого элемента в массиве, с которым reduce
играет, затем, если внутри этого reduce
этот большой вызов решит, что ему нужно n меньших вызовов, прежде чем он вернется к большим, тогда reduce
не следует переходить к следующему пункту, просто дождитесь его окончания.
Мой текущий код:
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.info( 'Got this error:' + error);
});
return import_files_download;
}
Где Base_Ajax_Requester
— это вспомогательный класс, который обрабатывает запросы AJAX и возвращает Promise
, когда это делается, чтобы вокруг него можно было написать код.
Мой fake_data
это:
let fake_data = {
'demo_handle' : 'demo-2',
'steps_to_import' : [ 'elementor-hf','post', 'nav_menu' ]
}
Как видите, fake_data.steps_to_import.reduce(..
пройдет через эти 3 значения, для каждого из них вызовите download_demo_file
, дождитесь его окончания, затем перейдите к следующему. Можно сказать, что я хотел бы, чтобы между elementor-hf
и post
было n меньших вызовов.
Первоначальный вызов download_demo_file
виден там, вот что всегда будет возвращаться из серверной части:
{
'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.
}
Повторный вызов, опять же, с download_demo_file
будет выглядеть так:
{
'demo_handle' : 'demo-2',
'step' : next_step_identifier,
'file_counter' : 2 //Dynamic. This will signal that it needs to now download file-2.xml.
}
...и так далее, пока сервер не отправит remaining_number_of_files : 0
, затем все останавливается, потому что больше нет файлов для загрузки, и можно перейти к следующему большому вызову.
Как я могу этого добиться?
Внутри каждого обратного вызова reduce
я бы сделал функцию, которая вызывает download_demo_file
(с изменяющимся file_counter
), и после того, как этот Promise разрешается, рекурсивно возвращает вызов самого себя, если remaining_number_of_files > 0
. Это будет означать, что getProm()
будет постоянно вызывать себя до тех пор, пока условие remaining_number_of_files > 0
больше не будет выполняться, и что только после этого будет разрешен весь промис для этой конкретной reduce
итерации.
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.info('Got this error:' + error);
});
Код, вероятно, будет много легче читать и понимать с async
/await
, хотя:
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?
Перехватите потребителя асинхронной функции.
Кроме того, ваш код теоретически работает, но на данный момент это не так, потому что я не передаю новые параметры (поэтому система не может сказать, что появился новый флаг file_counter
. Не могли бы вы обновить код, и я знаю, что это у меня много вопросов, но я обещаю, что это будет использовано с пользой - не могли бы вы немного объяснить, что происходит?
Должно быть достаточно просто, просто следите за file_counter
, который увеличивается.
Безусловно! Но в ? getProm()
, если я передам этот новый параметр, ничего не произойдет. Мысли?
Поскольку file_counter
находится во внешней области (обратного вызова .reduce
), не должно быть необходимости передавать параметр file_counter
(хотя я думаю, вы можете сделать это, если хотите) - он увеличивается внутри .call()
.then
, и также виден download_demo_file
Похоже, вы используете ES6 — если вы не используете Babel или уже ориентируетесь на среды ES6+, вы можете использовать await
путем транспиляции безregeneratorRuntime
, если это проблема. Преобразование относительно простой из функции async
в генератор.
Давайте продолжить обсуждение в чате.
Я не могу использовать
async/await
из-за ненадежности ограничений совместимости и полифиллов.