Имя: Пароль:
IT
Веб-мастеринг
Задача: JavaScript: Выполнить асинхронную функцию как синхронную в цикле
,
0 Патчкорд
 
09.11.15
06:09
Привет, друзья!
Вот такую задачку я придумал для вас:
Имеется функция, которая выполняется асинхронно. В цикле эта функция вызывается много раз, и получается что параллельно выполняется много вызовов этой функции.
Нужно переделать цикл так, чтобы в любой момент времени выполнялся только один эземпляр функции, то есть, нужно сделать так, чтобы очередной запуск этой функции в цикле происходил только после завершения предыдущего запуска.

Например:

<script>
for(var i=1; i<5; i++){
    setTimeout(function(i) { alert(i) }, 5000, i)
}
</script>
1 Патчкорд
 
09.11.15
06:10
Сюда буду перенаправлять троллей:

Условие задачи менять и упрощать нельзя
2 los_hooliganos
 
09.11.15
07:11
Ну пусть функцию выполняет конкретный объект.
3 Патчкорд
 
09.11.15
07:22
(2) Не понятно. Это как?
4 Лефмихалыч
 
09.11.15
08:09
Откажись от цикла. Каждую следующую итерацию должна инициировать сама эта функция в зависимости от условий.
5 Патчкорд
 
09.11.15
08:11
(4) В общем-то да. Только как бы это организовать?
6 Лефмихалыч
 
09.11.15
08:29
(5) готовый код пиши сам
7 vde69
 
09.11.15
08:37
типа такого...

<script>
for(var i=1; i<5; i++){
    if RunTimeout() else setTimeout(function(i) { alert(i) }, 5000, i)
}
</script>
8 Патчкорд
 
09.11.15
08:52
(6) не правильный ответ.

(7) так будет выполнять первую функцию, а все остальные пропускать. Не пойдет.
9 vde69
 
09.11.15
09:15
(8) RunTimeout - определяет работает-ли асинхронно setTimeout

для этого можно использовать глобальную переменную
10 Патчкорд
 
09.11.15
10:03
(9) В первый раз при проверке RunTimeout setTimeout не работал, произошло выполнение того что после else,
Во все последующие разы при проверке RunTimeout выясняется что setTimeout работает, и то что после else пропускается.

Не пойдет.
11 los_hooliganos
 
09.11.15
10:13
(8) Напиши функцию у класса. У класса вызови конкретный объект.
Так не бывает, что конкретный объект будет одновременно выполнять 2 одинаковые функции.
12 Патчкорд
 
09.11.15
10:17
(11) И из функции объекта вызвать мою асинхронную функцию?
Все равно получится что несколько функция параллельно запустятся
13 Гёдза
 
09.11.15
10:19
В js есть блокирующие очереди или подобное?
14 Гёдза
 
09.11.15
10:20
(13) Однако есть
https://gist.github.com/jwalsh/5723877
15 orefkov
 
09.11.15
10:23
Это же простая функциональщина - развернуть цикл в рекурсию

function startWork(count) {
    if(count == 0)
        return;
    doMyMainWork();
    setTimeout(function(){startWork(count - 1)});
}
setTimeout(function(){startWork(10)});
16 orefkov
 
09.11.15
10:31
Даже вот так, учитывая работу setTimeout и условия задачи

function startWork(count) {
    if(count == 5)
        return;
    doMyMainWork();
    setTimeout(startWork, 5000, count + 1);
}
startWork(startWork, 5000, 0);
17 orefkov
 
09.11.15
10:34
И вот последний, отшлифованный вариант
function startWork(count, maxCount) {
    if(count == maxCount)
        return;
    doMyMainWork();
    setTimeout(arguments.callee, 5000, count + 1, maxCount);
}
setTimeout(startWork, 5000, 0, 5);
18 Патчкорд
 
09.11.15
10:35
(16) Круто. Почти соответствует условию. С одним НО:

(0) Нужно переделать цикл так, чтобы в любой момент времени выполнялся только один эземпляр функции, то есть, нужно сделать так, чтобы очередной запуск этой функции в цикле происходил только после завершения предыдущего запуска.


По твоему способу получается что функция еще не завершилась, но уже вызывает следующую функцию.

так что не пойдет.
19 orefkov
 
09.11.15
10:38
(18)
Нифига.
Вся работа выполняется в doMyMainWork(), соответственно, при запуске следующего "тика" рабочая функция закончилась :)
20 orefkov
 
09.11.15
10:39
+(19)
А всё остальное - это "обвязка" вокруг рабочей функции, функциональный аналог цикла for.
21 Патчкорд
 
09.11.15
10:40
(19) вообще вся работа выполняется в асинхронной функции, ее роль в условии задачи для примера играет выражение "setTimeout(function(i) { alert(i) }, 5000, i)"
22 orefkov
 
09.11.15
10:42
(21)
Функция не бывает асинхронной, бывает асинхронный вызов функции. Что как-раз и происходит, когда ты вызываешь setTimeout - она осуществляет асинхронный вызов переданной в setTimeout функции. Не мешало бы вам для начала матчасть подучить, чтобы для себя разобраться, "кто на ком стоял".
23 Патчкорд
 
09.11.15
10:42
По твоей аналогии, изначально программа так выглядит:

for(var i=1; i<5; i++){
    doMyMainWork(i)
}
24 orefkov
 
09.11.15
10:45
(23)
Извиняюсь. Дошло. Ща подумаю.
25 Патчкорд
 
09.11.15
10:46
(24) у doMyMainWork(i) есть функция сallback, которая сработает через какое-то время.

В примере (0) это "function(i) { alert(i) }"
26 orefkov
 
09.11.15
10:57
Подумал. Запустил твой код. Запустил свой код.
Делает ровно то, что ты просил.
У тебя асинхронно запускается пять вызовов function(i) { alert(i) }.
Через пять секунд они все сразу "одновременно" срабатывают.
Мой код пять раз каждые пять секунд вываливает алерт.
Напиши, сам-то ты какой конечный результат ждешь?
27 Патчкорд
 
09.11.15
11:14
(26) твоя основная функция которая вызывается асинхронно - это startWork(count)

Она вызывается до завершения каждой предыдущей. Это нужно исправить.

doMyMainWork(i) - эта функция не понятно что делает, очевидно она не асинхронная и не имеет отношение к делу.
28 Гёдза
 
09.11.15
11:15
doMyMainWork = alert
29 Патчкорд
 
09.11.15
11:16
(28) а должно быть doMyMainWork = setTimeout...
30 Патчкорд
 
09.11.15
11:17
где вместо setTimeout могла бы быть совершенно любая функция которая возвращает управление в основную программу до полного своего выполнения
31 Патчкорд
 
09.11.15
11:23
Но в любом случае, какая бы ни была эта функция, добавление еще одного setTimeout решит эту проблему.
Можете дальше не копать.
32 orefkov
 
09.11.15
11:31
(27)
Ты тупишь что-ли?
startWork(count) - это не основная функция.
Это вспомогательная функция, которая выполняет роль "for" в функциональном стиле и внутри себя запускает твой setTimeout.
Это обёртка снаружи.
33 Гёдза
 
09.11.15
11:32
Может в цикле есть еще какие действие помимо таймаута?
34 Патчкорд
 
09.11.15
11:38
(32) >> Ты тупишь что-ли?
Нет, не я

>> внутри себя запускает твой setTimeout
нет, у меня другой setTimeout. Ты там его переделал, получилось совсем не то что было у меня.
setTimeout - это пример асинхронной функции. Замени его на другую, например, на xmlhttp.send(), и твой пример перестанет работать.
35 Патчкорд
 
09.11.15
11:42
(33) там может быть что угодно. Таймаут - просто для примера.
36 vde69
 
09.11.15
11:42
я не понимаю, почему внутри асинхронной функции не использовать глобальную переменную примерно так

процедура МояАсинхроннаяХрень()
если глФлаг Тогда
  возврат;
конецесли
глфлаг = истина;

....
....
// тут делаем черное дело
....

глфлаг = ложь;
КонецПроцедуры
37 Патчкорд
 
09.11.15
11:44
(36) я же тебе в (10) написал почему так нельзя. Ты просто не выполнишь часть вызовов из-за того что их пропустишь.
38 vde69
 
09.11.15
11:48
(37) ты не умеешь читать.... почитай внимательно (36)

твоя асинхронная процедура будет выполнятся столько раз сколько ее вызовешь, но вот зловредные вызовы будут выполнятся только после завершения предыдущих.

я такой код использую в ардуино для отображения индикаторов 1 раз в секунду не смотря на то, что код вызывается из главного цикла который критит десятки цисяч циклов в секунду
39 Патчкорд
 
09.11.15
12:09
(38) Еще раз объясняю: при таком вызове, функция будет работать криво, потому что код:

// тут делаем черное дело

будет выполняться только в первом вызове, а во всех последующих не будет выполнен, потому что сработает условие:

если глФлаг Тогда
  возврат;
40 Патчкорд
 
09.11.15
12:13
Например, у меня пользователь перетащил 500 фотографий в высоком разрешении. В цикле эти все фотографии обрабатываются, меняется размер фото для уменьшения размера. Одна фотка обрабатывается допустим 1 секунду. При таком вызове как у тебя, первая фотка начнет обрабатываться, а потом цикл пробежит 500 итераций почти мгновенно, и все последующие фотки будут не обработаны.

Для отображения индикаторов на твоем пылесосе это годится, а для моей задачи нет.
41 Гёдза
 
09.11.15
12:14
Через промисы пробовал делать?
42 Tateossian
 
09.11.15
12:18
(0) JavaScript is single threaded. There are no race conditions.

Нашел в описании. Выходит, выполнение скрипта однопоточное.
Всякие syncronized тут не нужны.
43 vde69
 
09.11.15
12:23
(39) а ты не заметил ? то есть при нормальном завершении флаг сбрасывается....

глфлаг = ложь;
КонецПроцедуры
44 Патчкорд
 
09.11.15
12:30
(43) Заметил, что флаг сбросится только в конце. А цикл не будет ждать. Он пробежит оставшиеся итерации раньше чем сбросится флаг.
45 orefkov
 
09.11.15
12:32
(34)
С чего это он вдруг перестанет работать, если setTimeout заменить на xmlhttp.send()?
Запусти, проверь - точно так же будет работать.
46 Патчкорд
 
09.11.15
12:35
(45) Действительно работает. Ну ладно тогда, твоя правда оказалась.
47 orefkov
 
09.11.15
12:48
+(45)
function doItMaxCount(xh, callback, maxcount) {
    var count = 0;
    xh.onreadystatechange = function(){
        if (xmlhttp.readyState == 4) {
            callback(count);
            count++;
        }
        if (count == maxcount)
            return;
        xh.send();
    }
    xh.send();
}
var xmlhttp = new XMLHttpRequest;
xmlhttp.open("GET", "/url", true);
doItMaxCount(xmlhttp, function(i) {alert(i)}, 5);
48 Патчкорд
 
11.11.15
07:13
(47) Отличное решение. Молодец. Хорошо знаешь JS
Глупец, лишенный способности посмеяться над собой вместе с другими, не сможет долго выносить программирование. Фредерик Брукс-младший