Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 08.08.2017, 19:52
Интересующийся
Отправить личное сообщение для koha345 Посмотреть профиль Найти все сообщения от koha345
 
Регистрация: 16.04.2016
Сообщений: 28

Рекурсия и возврат Promise
Имеется следующий js-файлик:
$(document).ready(function() {
	
	$("#search_posts_by_groups_form2").submit(function(e){
		e.preventDefault();
		var groupArray = new Array();
		var token = $('#token')[0].value;
		if (token.trim() == ""){alert('Введите Токен');return false;}
		var delim = $('#search_delim')[0].value;
		var groups_id = $('#search_groups')[0].value;
		// Получаем список сообществ
		var groups_arr = groups_id.split(delim);
		var groupsSize = groups_arr.length;
		var k = 0;
		var checkSum = 0;
		var groupIDArray = new Array();
		$.each(groups_arr, function(i, group_id) {
			groupIDArray[k] = group_id;
			++k;
		});
		
		var dfd = initGroupInfo(token, groupArray, groupIDArray, 0);
		dfd.done(function(){
			console.log('Закончили. Можно запускать другую функцию!')
		});
		
		
	});
});

function initGroupInfo(token, groupArray, groupIDArray, currentGroupIDIndex){
	var dfd = $.Deferred();
	
	var group_id = groupIDArray[currentGroupIDIndex];
	var promisedCalbackGroup = callbackGroup(token, group_id);
	promisedCalbackGroup.done(
		function(data){
			var obj = data.response;
			if (obj){
				if (obj[0]){
						group_id = groupIDArray[currentGroupIDIndex];
						groupArray[group_id] = new Object();
						groupArray[group_id].group = obj[0];
						console.log('Загрузка информации о группе: ' + group_id);
						if ( (groupIDArray.length - currentGroupIDIndex) > 1){
							initGroupInfo(token, groupArray, groupIDArray, ++currentGroupIDIndex);
						}else{
							console.log('Загрузка информации о группах завершена');
							dfd.resolve();
						}
				}else{
					console.log('Ошибка: initGroupInfo(2)');
				}
			}else{
				console.log('Ошибка: initGroupInfo(1)');
			}
		}
	);
	return dfd.promise();
};

function callbackGroup(token, group_id){
	return $.ajax({
		url: 'https://api.vk.com/method/groups.getById?v=5.52&access_token=' + token + '&group_id=' + group_id,
		type: 'GET',
		async: true,
		dataType: 'jsonp',
		crossDomain: true
	});
};


Я пытаюсь вызвать асинхронные запросы последовательно. Имеется список групп. Необходимо последовательно вызвать ajax и обработать полученные результаты так, чтобы выполнение следующего ajax-запроса начиналось после завершения обработки выполнения результатов предыдущего запроса. Помимо этого я хочу отследить выполнение initGroupInfo, чтобы после него запустить другую функцию. Проблема в том, что у меня мало знаний по promise и deffered.
Я не представляю, как правильно вызвать resolve и потому код не работает. Я гуглил, но те примеры, которые я встречал не имеют ничего общего с моей ситуацией.
Ответить с цитированием
  #2 (permalink)  
Старый 08.08.2017, 21:42
Интересующийся
Отправить личное сообщение для koha345 Посмотреть профиль Найти все сообщения от koha345
 
Регистрация: 16.04.2016
Сообщений: 28

Сообщение от Rise Посмотреть сообщение
koha345,
Одной темы мало что-ли надо три создать?
одна проблема = одна тема.
Ответить с цитированием
  #3 (permalink)  
Старый 08.08.2017, 22:02
Интересующийся
Отправить личное сообщение для koha345 Посмотреть профиль Найти все сообщения от koha345
 
Регистрация: 16.04.2016
Сообщений: 28

Сообщение от Rise Посмотреть сообщение
koha345,
А должно быть так: одна задача = одна тема, и в этой теме обсуждаешь свои проблемы по задаче.
Договорились.
Ответить с цитированием
  #4 (permalink)  
Старый 09.08.2017, 20:19
Интересующийся
Отправить личное сообщение для koha345 Посмотреть профиль Найти все сообщения от koha345
 
Регистрация: 16.04.2016
Сообщений: 28

Сообщение от Rise Посмотреть сообщение
А до этого какая функция была?
initGroupInfo
Ответить с цитированием
  #5 (permalink)  
Старый 09.08.2017, 23:48
Аватар для Белый шум
Профессор
Отправить личное сообщение для Белый шум Посмотреть профиль Найти все сообщения от Белый шум
 
Регистрация: 19.01.2012
Сообщений: 505

Если я правильно понял ваш код, то не хватает возврата результата dfd по всей цепочке рекурсии. И реджектов в случае ошибки:
if ( (groupIDArray.length - currentGroupIDIndex) > 1){
                            *!* dfd = */!* initGroupInfo(token, groupArray, groupIDArray, ++currentGroupIDIndex);
                        }else{
                            console.log('Загрузка информации о группах завершена');
                            dfd.resolve();
                        }
                }else{
                    console.log('Ошибка: initGroupInfo(2)'); *!* dfd.reject(); */!*
                }
            }else{
                console.log('Ошибка: initGroupInfo(1)'); *!* dfd.reject(); */!*
            }

Ну и обработка ошибки:
dfd.done(function(){
            console.log('Закончили. Можно запускать другую функцию!')
        })
        *!* .fail(function(){ */!*
            *!* console.log('ошибка'); */!*
        *!* }); */!*

Хотя лучше бы вы выложили ваш пример на https://jsfiddle.net/ чтобы там можно было сразу проверить.

Последний раз редактировалось Белый шум, 09.08.2017 в 23:54.
Ответить с цитированием
  #6 (permalink)  
Старый 10.08.2017, 05:56
Интересующийся
Отправить личное сообщение для koha345 Посмотреть профиль Найти все сообщения от koha345
 
Регистрация: 16.04.2016
Сообщений: 28

Сообщение от Белый шум Посмотреть сообщение
Если я правильно понял ваш код, то не хватает возврата результата dfd по всей цепочке рекурсии. И реджектов в случае ошибки:
if ( (groupIDArray.length - currentGroupIDIndex) > 1){
                            *!* dfd = */!* initGroupInfo(token, groupArray, groupIDArray, ++currentGroupIDIndex);
                        }else{
                            console.log('Загрузка информации о группах завершена');
                            dfd.resolve();
                        }
                }else{
                    console.log('Ошибка: initGroupInfo(2)'); *!* dfd.reject(); */!*
                }
            }else{
                console.log('Ошибка: initGroupInfo(1)'); *!* dfd.reject(); */!*
            }

Ну и обработка ошибки:
dfd.done(function(){
            console.log('Закончили. Можно запускать другую функцию!')
        })
        *!* .fail(function(){ */!*
            *!* console.log('ошибка'); */!*
        *!* }); */!*

Хотя лучше бы вы выложили ваш пример на https://jsfiddle.net/ чтобы там можно было сразу проверить.
Выложить не могу, к сожалению.
Конечное сообщение после выполнения initGroupInfo не выходит. Мне кажется, в ходе рекурсии первый вызов не вызывает resolve. Т.е. deffered теряется и никогда не вызывается, а на последнем шаге он вызывается, но возвращается в предыдущий вызов, но там никакого обработчика нет.
Написал вот так, но такой способ вернет ответ после первого вызова. Получается, надо как-то передавать предыдущий deffered.
(Код ниже вернет результат после первого же вызова)
function initGroupInfo(token, groupArray, groupIDArray, currentGroupIDIndex){
	var dfd = $.Deferred();
	
	var group_id = groupIDArray[currentGroupIDIndex];
	var promisedCalbackGroup = callbackGroup(token, group_id);
	promisedCalbackGroup.done(
		function(data){
			var obj = data.response;
			if (obj){
				if (obj[0]){
						group_id = groupIDArray[currentGroupIDIndex];
						groupArray[group_id] = new Object();
						groupArray[group_id].group = obj[0];
						console.log('Загрузка информации о группе: ' + group_id);
						if ( (groupIDArray.length - currentGroupIDIndex) > 1){
                            var dfd1 = initGroupInfo(token, groupArray, groupIDArray, ++currentGroupIDIndex);
							dfd1.done(
								new function(){
									dfd.resolve();
								}
							);
                        }else{
                            console.log('Загрузка информации о группах завершена');
                            dfd.resolve();
                        }
                }else{
                    console.log('Ошибка: initGroupInfo(2)'); dfd.reject();
                }
            }else{
                console.log('Ошибка: initGroupInfo(1)'); dfd.reject();
            }
		}
	);
	return dfd.promise();
};
Ответить с цитированием
  #7 (permalink)  
Старый 10.08.2017, 05:58
Интересующийся
Отправить личное сообщение для koha345 Посмотреть профиль Найти все сообщения от koha345
 
Регистрация: 16.04.2016
Сообщений: 28

Сообщение от Rise Посмотреть сообщение
koha345,
Такой итератор:
function iterator(array) {
	var index = 0, cycle = 0;
	return {
		next: function () {
			if (index >= array.length) index = 0, cycle++;
			return {
				cycle: cycle,
				index: index,
				value: array[index++]
			}
		}
	}
}

var groups = ['a', 'b', 'c'];

var iterable = iterator(groups);

for (var i = 6; i--;) console.log(iterable.next()); // test

Как можно использовать:
function request(current) {
	$.ajax({
		complete: function (jqXHR, textStatus) {
			if (current.cycle < 2) {
				if (current.cycle == 0) {
					console.log('task 0', current.value, current.index);
				}
				if (current.cycle == 1) {
					console.log('task 1', current.value, current.index);
				}
				request(iterable.next());
			} else {
				console.log('no tasks');
			}
		}
	});
}

request(iterable.next());
Интересное решение. Попробую реализовать при помощи данного подхода. Отпишусь, когда закончу.
Ответить с цитированием
  #8 (permalink)  
Старый 10.08.2017, 08:22
Аватар для Белый шум
Профессор
Отправить личное сообщение для Белый шум Посмотреть профиль Найти все сообщения от Белый шум
 
Регистрация: 19.01.2012
Сообщений: 505

Сообщение от koha345
Мне кажется, в ходе рекурсии первый вызов не вызывает resolve. Т.е. deffered теряется и никогда не вызывается, а на последнем шаге он вызывается, но возвращается в предыдущий вызов, но там никакого обработчика нет.
Да, что-то я не то предложил. Попробуйте передавать в рекурсивную ф-ю текущий dfd параметром, как-то так:
function initGroupInfo(token, groupArray, groupIDArray, currentGroupIDIndex *!* , dfd_out = false */!* ){

initGroupInfo(token, groupArray, groupIDArray, ++currentGroupIDIndex *!* , dfd */!* );
}else{
    console.log('Загрузка информации о группах завершена');
    if(dfd_out)  *!* dfd_out.resolve(); */!*

if(dfd_out) *!* dfd.done(function(){ dfd_out.resolve(); }); */!*
return dfd.promise();

Последний раз редактировалось Белый шум, 10.08.2017 в 08:30.
Ответить с цитированием
  #9 (permalink)  
Старый 12.08.2017, 09:27
Интересующийся
Отправить личное сообщение для koha345 Посмотреть профиль Найти все сообщения от koha345
 
Регистрация: 16.04.2016
Сообщений: 28

Сообщение от Rise Посмотреть сообщение
koha345,
Обновил пост.
Посмотрел. Не то, что я хотел, но это подкинуло мне мысль, которую я довел до конца.

К сожалению, уже сил и желания нет сидеть с этим Deffered. Нашкодил массив из вызываемых функций и пихаю в функцию, которая управляет очередью. В вызываемых функциях после ajax-запроса устанавливаются управляющие флаги и вызывается функция управления очередью, которая по полученным флагам принимает решение: запустить очередную итерацию или переходить к следующей функции.
В конечном счете я добился своего: создается иллюзия, будто запустил скрипт последовательно.
Нашкодил много, что выкладывать подобное даже не хочется.
За трату своего времени всем спасибо. В репутации отмечу.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Порядок выполнения функций с Promise и без. Даниленко Денис Общие вопросы Javascript 0 29.04.2017 12:33
Как асинхронно вызвать resolve() вне Promise? Malleys Общие вопросы Javascript 8 18.12.2016 08:00
Возврат на нужную вкладку oopros Общие вопросы Javascript 1 24.09.2016 14:39
Promise как сделать синхронным? FireVolkhov Angular.js 3 07.04.2015 16:36
Баг Aurora promise catch Octane Оффтопик 2 28.03.2014 01:37