Очередь событий дротика и микрозадача
Я пытаюсь понять, как работает цикл событий дартс. Я прочитал статью о цикле событий с веб-сайта The Event Loop and Dart, и автор довольно хорошо объясняет, как работает цикл обработки событий в dart.
Но что я не понимаю, так это то, как событие попадает в очередь. Например
new Future(() => 21)
.then((v) => v*2)
.then((v) => print(v));
Будет ли здесь Дарт создать три записи в очереди событий или только одну? Я знаю, что класс Future отвечает за задержку выполнения и когда я создаю объект из него, как
new Future(() => 21)
это будет только одна запись в цикле событий.
В этой статье, о которой я упоминал выше, я читал о микротаске. Эта микрозадача будет выполняться перед очередью событий, но я не вижу никакого смысла, почему команда dart реализует эту микрозадачу? Может быть, мне нужен пример!
4 ответа
После некоторого расследования выясняется, что правильный ответ - "они будут выполнены в следующем цикле событий"
Чтобы проверить это, вы можете написать что-то вроде этого:
import "dart:async";
void main() {
new Future(() {
scheduleMicrotask(()=>print("before loop 1"));
print("in event loop");
}).then((_) {
scheduleMicrotask(()=>print("before loop 2"));
print("in event loop");
}).then((_) {
scheduleMicrotask(()=>print("before loop 3"));
print("in event loop");
});
}
он должен вывести:
в цикле событий
в цикле событий
в цикле событий
до цикла 1
до цикла 2
до цикла 3
Хотя я не уверен, что вы не можете сломать эту оптимизацию. Так что единственно верным фактом является то, что первыйFuture
завершит первый, а второй - второй.
РЕДАКТИРОВАТЬ: странная часть (устарела):
С таким кодом:
import "dart:async";
void main() {
new Future(() {
scheduleMicrotask(print("before loop 1"));
print("in event loop");
}).then((_) {
scheduleMicrotask(print("before loop 2"));
print("in event loop");
}).then((_) {
scheduleMicrotask(print("before loop 3"));
print("in event loop");
});
}
вывод:
before loop 1
in event loop
before loop 2
in event loop
before loop 3
in event loop
Unhandled exception:
The null object does not have a method 'call'.
NoSuchMethodError: method not found: 'call'
Receiver: null
...
Но с этим:
import "dart:async";
void main() {
new Future(() {
scheduleMicrotask(()=>print("before loop 1"));
print("in event loop");
}).then((_) {
scheduleMicrotask(()=>print("before loop 2"));
print("in event loop");
}).then((_) {
scheduleMicrotask(()=>print("before loop 3"));
print("in event loop");
});
}
вывод:
в цикле событий
в цикле событий
в цикле событий
до цикла 1
до цикла 2
до цикла 3
EDIT2:
Я думаю, что понял. Во первых (неправильная версия) scheduleMicrotask
на самом деле никогда не планировалось должным образом, но, поскольку у Дарта есть активное выполнение аргументов, он выполняет print()
тем не мение. Так что же происходит, что все Future
выполняется в следующем цикле событий и печатать весь текст. Вот почему вывод в порядке вызова:
до цикла 1
в цикле событий
до цикла 2
в цикле событий
до цикла 3
в цикле событий
а не в порядке расписания.
Когда вы делаете:
new Future(() => 21)
.then((v) => v*2)
.then(print);
- Сначала вы называете
new Future(...)
конструктор. Это создает объект Future и планирует таймер для выполнения функции, которую вы задаете в качестве аргумента. - Тогда вы звоните
then
, Это создает новое будущее (назовите это future#2) и добавляет слушателя в первое будущее. Нет запланированных событий. - Тогда вы звоните
then
снова. Это создает еще одно будущее (будущее № 3) и добавляет слушателя на будущее № 2. Нет запланированных событий. - Затем срабатывает таймер, и
()=>21
выполняется, и первое будущее завершается со значением 21. - Слушатель первого будущего исполняется немедленно. Что вызывает
(v)=>c*2
с 21, а затем завершает будущее # 2 со значением 42. - Слушатель в будущем # 2 будет немедленно выполнен. Что вызывает
print
с 42, который печатает 42 и возвращаетnull
, Это завершает будущее № 3 со значениемnull
,
Завершение в будущем в настоящее время осуществляется через функцию "распространения", которая пытается завершить как можно больше фьючерсов, пока их слушатели синхронны. Вот почему завершение одного будущего немедленно завершит другое, без каких-либо промежуточных микрозадач.
Очередь микрозадач состоит в том, чтобы ставить в очередь асинхронное выполнение, но избегать возврата к главному циклу событий до завершения этих микрозадач. Вы можете гарантировать, что некоторые связанные действия будут выполнены полностью, даже если асинхронное выполнение выполнено до выполнения других асинхронных задач / событий, поставленных в очередь в главной очереди.
Кажется, код выполняется из then
лайк (v) => v*2
снова выполняется внутри Future
так как then
всегда возвращает Future
,
с https://www.dartlang.org/articles/event-loop/
Очередь для микрозадачи необходима, потому что коду обработки событий иногда требуется выполнить задачу позже, но перед возвратом управления в цикл обработки событий. Например, когда наблюдаемый объект изменяется, он группирует несколько изменений мутаций и сообщает о них асинхронно. Очередь микрозадач позволяет наблюдаемому объекту сообщать об этих изменениях мутации, прежде чем DOM сможет показать несогласованное состояние.
То, как я интерпретирую это описание, не согласуется с результатами тестов в ответе @Jare.
Просто небольшая вещь, чтобы добавить к предыдущим ответам. Статья 'Event Loop' объясняет это поведение довольно хорошо:
Функция, которую вы передаете методу then() в Future, выполняется сразу после завершения Future. (Функция не ставится в очередь, она просто вызывается.)
( https://www.dartlang.org/articles/event-loop/)
Это означает, что в приведенных выше примерах всегда есть одно событие, но много микрозадач.