Возможно ли переполнение очереди задач цикла событий Javascript?
Можно ли определить границу, которая не должна пересекаться для того, чтобы приложение хорошо масштабировалось относительно использования планирования задач (пере)?
Вопросы:
- Есть ли определенная стоимость выполнения setTimeout? Пусть скажем 0,1мс или процессорное время? Безусловно, стоимость на порядок ниже, чем порождение потока в разных средах. А есть ли?
- Лучше избегать использования setTimout для микро-задач, которые занимают 1-2 мс?
- Есть что-то, что не нравится планировать? Например, я заметил какое-то голодание IndexedDb для блокировок записи при планировании извлечения Store и других вещей.
- Можно ли безопасно планировать операции DOM?
Я спрашиваю, потому что я начал использовать Scala.js и реализацию Rif Monifu, которая использует планирование в массовом масштабе. Иногда одна строка кода отправляет около 5 задач в очередь цикла событий, так что в основном я спрашиваю себя, есть ли что-либо подобное переполнению очереди задач, которое может снизить производительность? Я задаю этот вопрос, особенно при запуске тестовых пакетов, в которых сотни задач могут быть поставлены в очередь в секунду.
Что приводит к другому вопросу: можно ли перечислить случаи, когда нужно использовать планировщик RunNow/Trampoline и когда планировщик Queue/Async в отношении Rx? Мне интересно об этом каждый раз, когда я пишу такие вещи, как obs.buffer(3).last.flatMap{..}
который сам планирует несколько задач
1 ответ
Некоторые замечания о планировании в Monifu - Monifu пытается свернуть асинхронные конвейеры, поэтому, если нижестоящие наблюдатели являются синхронными по своей природе, то Monifu будет избегать отправки задач в Планировщик. Monifu также оказывает противодавление, поэтому оно контролирует, сколько задач отправляется в планировщик, поэтому вы не можете оказаться в ситуации, когда очередь браузера взорвется.
Например, что-то вроде этого... Observable.range(0,1000).foldLeft(0)(_+_).map(_ + 10).filter(_ % 2 == 0)
отправляет только одну задачу в планировщик для запуска этого начального цикла, в противном случае весь конвейер является полностью синхронным, если наблюдатель также является синхронным и не должен отправлять какие-либо другие задачи в этой очереди. И он отправляет первую задачу в очередь, потому что не имеет представления о том, насколько большим будет этот источник, и обычно подписка на источник данных выполняется в связи с некоторыми обновлениями пользовательского интерфейса, которые вы не хотите блокировать.
Есть 3 больших исключения:
- вы используете источник данных, который не поддерживает обратное давление (например, соединение через веб-сокет)
- у вас есть реальная асинхронная граница в приемниках (то есть наблюдателя), что может случиться, например, при взаимодействии с внешними службами, и это настоящее будущее, которое вы не знаете, когда оно будет завершено
Некоторые решения возможны...
- в случае, если обмен данными между серверами не поддерживает обратное давление, в таком случае проще всего изменить сервер для его поддержки - также нормальные HTTP-запросы естественным образом подвергаются обратному давлению (т. е. это так же просто, как
Observable.interval(3.seconds).flatMap(_ => httpRequest("..."))
- если это не вариант, у Monifu есть стратегии буферизации... так что вы можете иметь неограниченную очередь, но вы также можете иметь очередь, которая вызывает переполнение буфера и закрывает соединение, или буферизацию, которая пытается сделать обратное давление, вы также можете начать отбрасывать новые события, когда буфер заполнен, и я работаю над другой стратегией буферизации для отбрасывания более старых событий - с целью избежать перегруженных очередей
- если вы используете "слияние" с источниками, которые могут быть неограниченными, не делайте этого;-)
- если вы выполняете запросы к внешним службам, попробуйте оптимизировать их - например, если вы хотите отслеживать историю событий, отправляя их в веб-службу, вы можете группировать данные и выполнять пакетные запросы и т. д.
Кстати, в вопросе о браузере и планировании задач меня беспокоит только то, что Monifu недостаточно эффективно прерывает работу. Другими словами, он, вероятно, должен разбивать более длинные синхронные циклы на более мелкие, потому что хуже, чем страдания, являются проблемы с задержкой, видимые в пользовательском интерфейсе, потому что какой-то цикл блокирует ваши обновления пользовательского интерфейса. Я бы предпочел, чтобы в планировщике было представлено несколько небольших задач, а не больших. В браузере у вас в основном есть совместная многозадачность, все выполняется в одном потоке, включая обновления пользовательского интерфейса, что означает, что очень плохо иметь части работы, которые слишком долго блокируют этот поток.
Тем не менее, я сейчас нахожусь в процессе оптимизации и уделяю больше внимания времени выполнения Javascript. На setTimeout
он используется, потому что это более стандартно, чем setImmediate
Однако я сделаю некоторую работу по этим аспектам.
Но если у вас есть конкретные образцы, чья производительность отстой, сообщите об этом, так как большинство проблем можно исправить.
Ура,