|
Задача: 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
|
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |