Список изменений
Не выпущено
Заголовок раздела «Не выпущено»doc/reference.mdотправляет за таблицей контекста в справочникasync_job, куда таблица переехала из его README.pubspec.yamlназывает сайт документации, и pub.dev ссылается на него.- README и
doc/jobs.mdбольше не говорят, чтоenqueueбез аргументов типа работает в теле со всемS: тамctx.stateимеет типObject?. README теперь говорит и о том, что рабочий тип действует от старта задачи до её конца. - Новая страница
doc/riverpod.mdпоказывает контроллер под провайдером riverpod: мост между ними, камеру, которую закрывают при уходе экрана, форму, которую отправляют один раз, и то, чего контроллер там стоит. - Новая страница
doc/bloc.mdставит контроллер под bloc: мосты для его задач и его состояний и их цену, три требования, где контроллер снимает с bloc обходной приём, и сценарии, где ответ остаётся за bloc.
Этот пакет реэкспортирует async_job целиком, поэтому изменения ядра
принадлежат и этому пакету: см.
список изменений async_job.
Ломающие изменения
Заголовок раздела «Ломающие изменения»ctx.waitизasync_job, устаревший с 0.3.0 в пользуctx.abandonable, удалён.Policy.replaceпереименован вPolicy.keepLatest. Имя говорит, что остаётся в очереди: последняя из ждущих задач с этим ключом. Снятая задача по-прежнему кончаетсяCancelled(replaced)сReplacedCancelReason, аAccumulationPolicy.replaceсохраняет своё имя.HandlerCancelReasonизasync_jobпереименован вBodyCancelReason, а егоnameтеперьbody, а неhandler.SoloContext.eachвозвращаетSoloJob<void>, как иSoloContext.child. Класс, который сам реализуетSoloContext, тоже возвращает этот тип.
Добавлено
Заголовок раздела «Добавлено»Solo.debugпечатаетdropс задачей и еёCancelled, когда правила рабочего типа отклоняют задачу из очереди на старте.
Документация
Заголовок раздела «Документация»- README говорит, в каком выпуске с версии 1.0.0 выходит каждое изменение:
исправление в патч-выпуске, добавление в минорном, ломающее в мажорном, и что
soloвыходит с каждым минорным выпускомasync_job, на протоколе движка которого он построен. - README называет плагин анализатора
solo_lintsв разделе «Установка», а страница частых ошибок говорит, что о большинстве из них плагин сообщает. - Страница диагностики говорит, что печатает её наблюдатель и что наблюдатель, чей лог уходит с устройства, печатает только то, что приложение решило отдать.
- Быстрый старт README помещается на один экран: контроллер, загрузка
и обновление, отменённое до ответа, с тем, что печатает программа;
example/bin/quick_start.dartего запускает. Контроллер с ключом, политикой, обработчиками состояния и ожидаемыми ошибками, на котором строятся руководства, открывает страницу о задачах как раздел «Контроллер профиля». - Страница о задачах заканчивается разделом «Нажатие, которое не ждёт перезагрузки»: лайки ленты живут в своём контроллере, и лайк не ждёт за перезагрузкой ленты.
- README говорит, что новое значение перечисления или новый подкласс
запечатанного класса выходит в мажорном выпуске, называет перечисления
и запечатанные классы пакета и говорит, для кого
package:solo/listeners.dart. - README говорит, что задача не
Futureи что задача, которую правила отклонили на старте, кончаетсяCancelledсRulesCancelReason, а неFailed. - Справочник называет
cancelAllсforceиreason, поляSoloPending,SoloPhaseиSoloUnansweredHandler. - Страницы об отмене, ресурсах и ошибках дают весь путь ошибки, за которую
никто не ответил:
onUnanswered, затемSolo.unansweredHandlerили зона. Страница ошибок называет среди правил старта иkeepWhile, спрошенный на старте, и говорит, куда ошибка идёт дальше во Flutter-приложении. - Страницы о задачах, стримах, дочерних задачах, накоплении, камере и частых
ошибках говорят то, что нужно читателю примеров: что
queue,lastJobWhereиcurrentзащищены, чтоctx.eachвозвращаетSoloJob<void>, что такое_Op, почему накопитель живёт в полеlate final, какая строка камеры отменяет задачу и чтоcatch, который публикует состояние сбоя, на отменённой задаче ничего не даёт. - Страница об отмене говорит, что секция
uncancellableдержит и срок и что задача сcancellable: falseне принимаетtimeout: вызов, который передаёт оба, бросаетArgumentError.
Ломающие изменения
Заголовок раздела «Ломающие изменения»- Нижняя граница SDK — Dart 3.10.0, прежде была 3.6.0.
Solo.job,Solo.enqueue,Solo.collectиSolo.accumulateпринимают именованныйisExpectedError. Подкласс, который переопределяет один из них, обязан его принять.
Добавлено
Заголовок раздела «Добавлено»isExpectedErrorназывает ошибки, которых задача ждёт:onErrorслышит их сJobErrorAnswer.never, отвечать за них никого не спрашивают, а баг по-прежнему идёт к ответчикам и в зону. См. «Назвать ожидаемые ошибки».
Ломающие изменения
Заголовок раздела «Ломающие изменения»Solo.onErrorиSoloObserver.onErrorпринимают{required JobErrorAnswer answer}, какJobObserver.onErrorядра. См. «Отправка ошибки».- У
SoloContextпоявилсяchild,ctx.childядра: он делает задачу того же контроллера с рабочим типом иkeepWhileтой задачи, которая его сделала. Код, у которого есть толькоJobContext, делает им ребёнка, которого контроллер примет, аJob.deferredконтроллер отвергает. Класс, который сам реализуетSoloContext, его добавляет. См. «Дочерние задачи и цепочки» и список измененийasync_job.
Документация
Заголовок раздела «Документация»- Раздел «Отложенный старт» показывает, как начать
задачу позже, не держа очередь, с ожиданием, которое снимают
cancelAllиclose. - Раздел «Уведомление, которое принимает экран» показывает новость, которая ждёт на экране, пока пользователь её не закроет, и не держит работу контроллера.
Этот пакет реэкспортирует async_job целиком, поэтому изменения ядра
принадлежат и этому пакету: см.
список изменений async_job.
Документация
Заголовок раздела «Документация»- Когда группа готова, к какой группе присоединяется событие и как задача
группы начинается, отменяется, падает и заканчивается на
close(), ушло со страницы «Накопление событий» на «Правила накопления».
Этот пакет реэкспортирует async_job целиком, поэтому изменения ядра
принадлежат и этому пакету: см.
список изменений async_job.
Ломающие изменения
Заголовок раздела «Ломающие изменения»Что писать вместо прежнего, слева имя в 0.2.0; у каждого есть своя запись
ниже.
0.2.0 |
0.3.0 |
|---|---|
SoloBase<S> |
Solo<S> |
Solo<S>, класс со stream |
Solo<S> with SoloStream |
controller.state |
controller.currentState |
onError: у run и job |
ifFailed: |
onCancel: у run и job |
ifCancelled: |
run(body) |
enqueue(body) |
add(job) |
enqueueJob(job) |
Job<T> помощника, переданный в ctx.run |
SoloJob<T> |
onChange(previous, current) |
onChange(transition) |
SoloObserver.onChange(solo, previous, current) |
SoloObserver.onChange(solo, transition) |
SoloQueue.lastWhere |
Solo.lastJobWhere |
Job.ignore() |
Job.ignoreFailure() |
JobBase.debug |
Job.debug |
Solo.onErrorстал уведомлением, а за ошибку, которой больше ни у кого нет, отвечает новыйSolo.onUnanswered: за ту, которой не несёт ни один исход, и заFailed, который никто не наблюдает. Чтобы такая ошибка не дошла до зоны, задайтеSolo.unansweredHandler, этоSoloUnansweredHandler, или переопределитеonUnanswered: заданныйSolo.observerили переопределениеonError, которое не зовётsuper, больше этого не делают.Failed, который никто не наблюдает, уходит к обработчику или к переопределённомуonUnansweredчерез микрозадачу после конца задачи, а до зоны доходит, как в0.2.0, только когда не задано ни того, ни другого. Так же уходит и провал бросившегоcanStart. См. «Ответ за ошибку».Job.ignore()переименован вJob.ignoreFailure()вasync_job.JobBase.debugтеперь называетсяJob.debugвasync_job, чья главная библиотека больше не экспортируетJobBase.- Задача продолжается один раз, одним
thenили ручкамиshared(), вasync_job: второйthenна задаче изenqueueбросаетStateError. Задача, которуюPolicy.droppableможет отдать другому вызывающему, и задача группы накопителя отказывают простомуthenс первого вызова: каждый вызывающий продолжает её через ручкуshared(). См. «Очередь и политики» и «Запуск, отмена и ошибки». - Обработчики состояния у
enqueueиjobпереименованы:onErrorвifFailed,onCancelвifCancelled. SoloBaseпереименован вSolo, а прежнийSolo, класс, который нёс broadcast-стрим, стал миксиномSoloStream. Контроллер, у которого читают.stream, добавляетwith SoloStream. См. «Наблюдение за состоянием».Solo.runпереименован вenqueue, аSolo.add— вenqueueJob.runконтроллера ставил задачу в хвост очереди, аctx.runзапускает ребёнка сразу, мимо очереди;addделил имя сaddнакопителя, который принимает событие. См. «Создание и запуск задач».job,enqueueJob,enqueue,collectиaccumulateпомечены@protected. Дайте контроллеру метод на каждую операцию, которую вызывают снаружи. См. «Создание и запуск задач».SoloStream.publishпомечен@protected.- Хуки
onStart,onFinish,onError,onUnanswered,onLog,onChangeиonCloseпомечены@protected. SoloJob— этоChildJob, тип, который теперь принимаютctx.runиctx.runAllизasync_job. Задача изjob(...), которую держат какJob, там больше не компилируется: помощнику, который её делает, дайте типSoloJob, какSoloJob<void> _recordPath(String path) => job(...). Продолжение изthen, которомуctx.runотказывал сArgumentError, там тоже больше не компилируется. См. «Цепочки завершённой работы».- Синхронное чтение контроллера называется
currentState, а неstate. Тело читаетctx.state. См. «ПочемуcurrentState, а неstate». - Состояние закрытого контроллера окончательно. Когда движок закончил
закрытие,
externalSetStateбросаетStateError: защитой служит новыйSolo.isFinished, а последним местом, где запись проходит, стал новый хукSolo.onClose. См. «externalSetState». Solo.closeпринимаетSoloCloseMode. По умолчанию стоитSoloCloseMode.cancel, то есть то, чтоcloseделал всегда;SoloCloseMode.drainвыполняет то, что уже стоит в очереди, а не отбрасывает. См. «Три способа остановиться».- Хуки изменения принимают
SoloTransition<S>вместо пары состояний:Solo.onChange(transition)иSoloObserver.onChange(solo, transition). - Подкласс с членом, названным как новый член
Solo, перестаёт собираться или переопределяет его. Новые члены экземпляра:currentState,isFinished,isDraining,pending,enqueue,enqueueJob,onClose,onUnanswered,addListener,removeListener,hasListenersиonListenerError. - Класс, который сам реализует
SoloContext, добавляетabandonable,heldCancellation,pause,runAllиstart, новые членыJobContextвasync_job, егоrunпринимаетdisposeиdiscard, аeach—keyиdescribe. Policy.droppableотказывает ключу, который держит задача с другим типом результата, сArgumentError, и отказывает до того, как примет новую задачу. См. «Ключ на каждый запрос».- Задача, отброшенная
Policy.droppable, заканчивается с новой причинойDuplicateCancelReason, а не сManualCancelReason. - Задача, на место которой
Policy.replaceилиPolicy.restartставит более новую, заканчивается с новой причинойReplacedCancelReason, а не сManualCancelReason. collectиaccumulateпо умолчанию берутAccumulationPolicy.join, а неadjacent. Задача другого рода, вставшая в очередь между двумя событиями, больше не граница; назовитеadjacentтам, где граница нужна. См. «Команды, где важна только последняя».AccumulationPolicy.replaceпереносит задачу ждущей группы в хвост, вместо того чтобы строить новую задачу и отменять старую. Каждое событие одной группы теперь делит её хэндл и исход при любой политике.SoloQueue.lastWhereубран.Solo.lastJobWhereнаходит ту же задачу и смотрит ещё и на идущую.cancelAllиSoloQueue.remove,removeWhereиclearпринимаютreason.SoloQueue.jobsиSolo.currentотдаютSoloJob<Object?>, и его же принимают предикатыSoloQueue.removeWhereиSolo.lastJobWhere. Предикат, написанный дляJob<Object?>, собирается как прежде; переопределениеcurrentилиlastJobWhereи класс, который реализуетSoloQueue, берут новые типы.job,enqueue,collectиaccumulateпринимаютtimeout. Переопределение передаёт его дальше вsuper, иначеenqueue(timeout: ...)молча остаётся без срока.collectиaccumulateпринимаютifFailedиifCancelled. Переопределение передаёт их дальше вsuper, иначе группы его накопителей молча остаются без обработчиков состояния.
Устарело
Заголовок раздела «Устарело»ctx.waitвasync_jobустарел, его заменяетctx.abandonable; старое имя уйдёт в следующем выпуске.
Что вы увидите после обновления
Заголовок раздела «Что вы увидите после обновления»Ошибки, которые 0.2.0 теряла или оставляла хукам, теперь доходят
до Solo.unansweredHandler или, когда обработчик не задан, до зоны, где
создана задача. В тесте это зона теста, и тест падает:
- ошибка, которой не несёт ни один исход, в приложении, которое задаёт
Solo.observerили переопределяетonError, не зовяsuper; - провал тела задачи, которая кончилась
Cancelled: операция, которая останавливается на токене, отменяемом задачей черезctx.onCancel, бросает такой провал при каждой отмене.Job.isCancellationErrorодин раз, вmain, называет ошибки, которые вызывает отмена, и они доходят доonErrorи дальше не идут;job.ignoreFailure()глушит провал только своей задачи; - поздняя ошибка действия, которого тело не дождалось из-за
.timeoutилиFuture.any, пришедшая, когда задача уже кончилась; - провал собственной уборки источника, когда
ctx.eachотпускает стрим.
Задача, которая сдаётся через ctx.check() внутри вызова, который
ctx.abandonable уже отпустил, больше не доходит до onError как провал.
onError контроллера и наблюдателя слышит каждый провал и каждый Cancelled
внутри ParallelWaitError отдельным вызовом, а 0.2.0 отдавала ему всю ошибку
один раз. Failed с такой ошибкой, который никто не наблюдает, доходит
до Solo.unansweredHandler или до зоны по провалу и без своих отмен, а 0.2.0
отдавала зоне всю ошибку. См. «Ответ за ошибку».
Отброшенный дубликат печатает Cancelled(duplicate), задача, заменённая под
Policy.replace или Policy.restart, печатает Cancelled(replaced),
а несколько сообщений об ошибках сформулированы иначе.
Стектрейс задачи, отменённой её правилами, снимается там, где правила это заметили.
ctx.each спрашивает правила задачи до того, как запустить ребёнка, как это
делает ctx.state. Если задача сама опубликовала через emit состояние,
которое они отклоняют, ctx.each заканчивает её отменой по правилам,
и ifCancelled не вызывается, как на любой контрольной точке правил. 0.2.0
в этом случае возвращала ребёнка, которого те же правила не дали запустить:
стрим никто не слушал, задача, не прочитавшая ребёнка, кончалась Done,
а прочитавшая его .value кончалась отменой от ребёнка, и ifCancelled
вызывался. См. «Обработка стрима».
Тело задачи из очереди работает в зоне, где задачу создали, и с ним её срок
и хуки onStart. 0.2.0 запускала их в зоне того, что разбудило очередь, чаще
всего чужого вызова, и тело, ждавшее future своей зоны, зависало, если эта
future падала, а само тело работало под другим runZonedGuarded. См.
«Создание и запуск задач».
keepWhile не может записать состояние: запись, пока он отвечает, бросает
StateError, и правило бросает вместе с ней, как любое бросившее правило.
0.2.0 пропускала запись и снова спрашивала правило о новом состоянии,
и правило, которое писало при каждом вопросе, исчерпывало стек. canStart
по-прежнему может писать: его спрашивают один раз.
Обработчик ifFailed или ifCancelled, который возвращает полученное
состояние или const-значение, уже лежащее в состоянии, ничего не публикует
и оставляет на месте собственную запись; 0.2.0 публиковала это состояние ещё
раз как переход.
Строки Solo.debug о том, что задача встала в очередь, начинаются с enqueue,
а не с add.
Добавлено
Заголовок раздела «Добавлено»ctx.startизasync_jobзапускает ребёнка, которого тело не ждёт. Правила контроллера решают так же, как дляctx.run: ребёнок, которому они отказали, кончаетсяCancelled(rules), аstartвозвращается как обычно; бросившийcanStartбросает изstart. Провал ребёнка уходит вonUnanswered.timeoutдаёт задаче контроллера собственный срок. Его принимаютjob,enqueue,collectиaccumulate; когда срок выходит, задача заканчиваетсяCancelled(timeout)с новой причинойTimeoutCancelReasonизasync_job. См. «Срок задачи».collectиaccumulateпринимаютifFailedиifCancelled, обработчики состоянияjob. Группа, которая написала собственное состояние, пока работала, откатывает его через них, когда падает или её отменяют; она вызывает один из них не больше одного раза, сколько бы событий в ней ни было. См. «Запуск, отмена и ошибки».Job.isCancellationErrorизasync_jobназывает ошибки, которые вызывает отмена. Провал отменённой задачи, который он называет, доходит доonErrorи дальше не идёт: его не слышат ниonUnanswered, ниSolo.unansweredHandler. См. «Токен».Job.whenFinishedизasync_jobслышит конец задачи, не наблюдая её исход. Слушатель получает исход, а за провал, который никто не наблюдал, всё равно отвечают. См. «Задачи и результаты».ctx.eachконтроллера принимаетkeyиdescribeизasync_job, чтобы назвать своего ребёнка.keyздесь только имя: политики очереди правят корневыми задачами, а ребёнок не корневой.SoloObserver.allделает из нескольких наблюдателей одного дляSolo.observer. См. «Отмена, которая не доходит».Soloнесёт собственных слушателей:addListenerиremoveListener, а рядом с ними защищённыеhasListenersиonListenerError. См. «Наблюдение за состоянием».Solo.pendingговорит, что удерживает контроллер, когдаcloseне вернулся. ЭтоSoloPendingилиnull, когда ничто не удерживает, аSoloPendingбывает одним из трёх:SoloPendingJobсо своейSoloPhase,SoloPendingQueueилиSoloPendingStream. См. «Что удерживает контроллер».AccumulationTiming.throttleпринимаетstartAtOnce. СstartAtOnce: falseинтервал отсчитывается и перед первой группой.- У
AccumulationTimingесть==,hashCodeиtoString, который печатает вид, длительность иstartAtOnceв форме вызова конструктора. package:solo/listeners.dart:Listeners, список слушателей, по которому идёт уведомитель, для пакета, который строит собственную доставку поверхsolo.Solo.debugназывает идущую задачу в строке задачи, вставшей в очередь, пока та идёт:while Job(outer) is running: it starts after Job(outer) ends; так же говорит строка дубликата, который возвращаетPolicy.droppable, а дубликат идущей задачи кончается словамиwhich is running: if awaited from inside Job(x), it waits for itself.
Исправлено
Заголовок раздела «Исправлено»- Состояние, которое
canStartзаписал, пока отвечал, проверяется наWиkeepWhile: задача стартовала под ним, даже когда оно им уже не подходило. Policy.replaceиPolicy.restartставят новую задачу в очередь раньше, чем отменяют задачи, которые она замещает. Задача того же ключа, которуюonFinishилиwhenCancelledпоставили, когда замещённая задача кончалась, вставала раньше новой, и выполнялись обе: последний запрос проигрывал предыдущему.- Ребёнок, которого собственный
canStartотменил, отвечая «да», доходит до родителя своей отменой, как при отказе: родитель кончалсяFailedсоStateErrorо том, что ребёнок уже закончился. - Ребёнку, которого собственное правило старта запускает ещё раз, отказывают
с
StateError: родитель переставал ждать ребёнка, тело которого продолжало работать, аonDisposeребёнка не вызывался. Policy.droppableиSolo.lastJobWhereбольше не возвращают задачу, которая не собирается делать работу:cancelAll()и свежий запрос строкой позже отвечали на второй вызов хэндлом, который первый только что отменил.cancelAllи удаленияSoloQueueдостают до задачи, которую движок вот-вот запустит: вызванные из правил старта этой задачи, они её пропускали, и она всё равно стартовала.Policy.droppable,replaceиrestartдостают до задачи, которую движок вот-вот запустит: вызванные из правил старта этой задачи с её ключом, они её пропускали, и две задачи одного ключа шли одна за другой.publish, который бросает, больше не задерживает изменения, стоящие в очереди за ним, а правила работающих задач спрашиваются всё равно: задачу, которую новое состояние не устраивает, отменяют.- Предикат
SoloQueue.removeWhereилиSolo.lastJobWhere, который удаляет задачу, больше не натыкается наConcurrentModificationError. - Завершённая задача отпускает своё тело, свои обработчики состояния и задачу, которая её запустила: один сохранённый хэндл держал всё дерево завершённых задач, из которого он вышел.
- Задача, которую работа
ctx.unattendedодного контроллера запускает на другом контроллере, стартует в зоне, откуда работа была запущена: ошибка гологоunawaitedв её теле доходила доonErrorконтроллера, чья задача запустила работу. Cancelled, который бросает хук контроллера или наблюдателя, больше не доходит до зоны.
Документация
Заголовок раздела «Документация»- README стал стартовой страницей, а справочная часть переехала в
doc/, по странице на тему: «Задачи и очередь», «Состояние», «Отмена», «Ресурсы и освобождение», «Дочерние задачи и цепочки», «Стримы», «Диагностика», «Ошибки», «Тестирование» и «Пример камеры». Раздел о Flutter ушёл вflutter_solo. - Две страницы для справки: «Справочник», где у каждого члена
Solo, его контекста, задач, очереди и накопителей и у каждого параметраenqueue,job,collectиaccumulateуказан раздел с его объяснением, и «Частые ошибки», семь ошибок и правильный способ для каждой. - Одна рекомендация
0.2.0заменена: источник внешних состояний останавливают вonClose, а не доclose, а колбэк, который может прийти и после этого, защищает свою запись проверкойisFinished. - Пример камеры больше не застревает в
Preparing, когда открытие отменено на полпути. - Пример камеры больше не публикует
Readyповерх сбоя, о котором устройство сообщило, пока камера открывалась. - Каждый сценарий страницы «solo и bloc рядом» кончается строкой о том, что даёт solo.
Первый опубликованный выпуск. 0.1.0 так и не покинула дерево, поэтому ни одна запись ниже не требует миграции; то, что изменилось после него, стоит в конце, для дерева, которое следило за пакетом до его выхода.
-
Добавлены обработчики состояния
onErrorиonCancelуrunиjob. Они применяют состояние после детей и уборки, до следующей задачи в очереди, и сохраняют исход с провалом или отменой. Несовместимые внешние изменения состояния подавляют поправку, в том числе во время уборки и для потомков. Быстрый старт теперь использует sealed-состояния загрузки, результата и ошибки. -
Добавлены
AccumulationTiming.debounceи.throttleдляcollectиaccumulate. Debounce запечатывает группу после паузы между событиями; throttle разносит фактические старты групп. Ждущие группы пропускают готовые задачи, а обработчики, дети и уборка остаются последовательными.collectхранит каждое событие;accumulateхранит результатmerge. Закрытие отменяет таймерыtimingи группы в очереди без финального пакета. -
Ломающее:
ctx.run(child)возвращает результат ребёнка какFuture<T>. Заменитеawait ctx.run(child).valueнаawait ctx.run(child); сохраняйте исходный хэндл ребёнка, чтобы отменять его или смотреть его исход. После успехаrunпроверяет отмену родителя и правила состояния, прежде чем вернуться. Параллельные старты должны обработать возвращённую future или явно её проигнорировать.runконтроллера иctx.eachпо-прежнему сразу возвращают хэндлы задач. -
Добавлены фабрики
collectиaccumulate, которые возвращаютSoloAccumulator: они собирают события в список или сливают их в одно значение до выполнения.AccumulationPolicyвыбирает группировку соседних, замену в хвосте очереди с переносом данных или присоединение к группе, которая уже стоит в очереди. Группы живут по обычному жизненному циклуSoloJob, с правилами состояния и отменой. -
Из ядра реэкспортированы
Job.thenиChainCancelReason. Продолжения поддерживают цепочки отмены и имеют собственныйJobContext; они не наследуют состояние контроллера, правила, наблюдателей и место в очереди. -
Движок
SoloBase<S>: одна корневая задача за раз, исключительное владение состоянием. -
Solo<S>с broadcast-стримомstream, доставка на следующей микротаске. -
Задачи как
async-тела с рабочим типомW, правиламиcanStartиkeepWhile,cancellable: false. -
Замена, отменённая синхронным слушателем, в очередь не попадает и потому не может поглотить более позднюю задачу
droppable. -
Требуется
async_job: ^0.2.0:job.whenCancelled(callback)регистрирует синхронного слушателя, который получаетCancelled, и возвращает функцию, которая его снимает. -
Хэндл
Job<T>:done,value,outcome,whenCancelled,cancel,ignore. -
Outcome<T>:Done,Failed,CancelledсCancelReason. -
Исход
Failed, который никто не наблюдал, уходит в зону, создавшую задачу, так же, как Dart сообщает о необработанной ошибкеFuture. Чтениеdoneилиvalueи вызовignoreсчитаются наблюдением;Cancelledдо зоны не доходит никогда. -
JobContextядра:check,wait,join,uncancellable,unattended,onCancel,run,each,log. -
SoloContext<S, W>поверх него, где живёт состояние:state,stateAs,emit. -
Тело не пишет
awaitсамо по себе: каждый вызов идёт через контекст, и член, который оно выбирает, говорит, что отмена делает с этим вызовом.waitкончает ожидание и даёт действию идти дальше;joinждёт действие целиком и сдаётся после него, так что вызов устройства никогда не бросается на полпути;uncancellableудерживает отмену на время вызова и сдаётся, когда шаг кончился;unattendedне ждёт вовсе и отдаёт работу движку, который слышит её провал даже после конца задачи. -
ctx.unattended(action)для работы, которую тело запускает и не ждёт. Она идёт в собственной зоне ошибок, и всё, что она оставит неперехваченным, сейчас или много позже конца задачи, доходит доonError, а не до процесса. -
Изменение поведения. Без
SoloBase.observerи без переопределенияSoloBase.onErrorошибка, которой больше некуда идти, уже не останавливается в пустом хуке: она уходит в зону, где создана задача, так же, как ядро сообщает о ней, когда у задачи нет наблюдателя. Это касается уборки, колбэкаonCancel, поздней ошибки брошенного вызова или работы, отданнойunattended, иcanStartилиkeepWhile, которые бросили вместо ответа, пока задача идёт.Cancelledсоставляет единственное исключение и туда не уходит никогда. Поставьте наблюдателя или переопределите хук, и путь снова ваш; чтобы сохранить его, вызовитеsuper.onError(...)из переопределения. Правило, которое бросает до старта задачи, на этом пути вовсе не стоит: оно становится исходомFailedи доходит до зоны, как всякий ненаблюдённый провал, каким бы ни был наблюдатель. -
Стек уборки вместо
finallyв теле:ctx.onDisposeосвобождает при любом исходе,ctx.onDiscardтолько когда задача кончается неDone, аwaitиjoinпринимают те же два какdisposeиdiscardдля значения, которое они передают: соединение или файл, открытые брошенным действием, всё равно закрываются. Движок разматывает стек после детей и до исхода, иcloseего ждёт;ctx.disown(value)забирает регистрацию обратно, когда тело передаёт значение само. -
JobContext.uncancellableсоставляет пару кwait: он выполняет шаг, который нельзя взять назад (платёж на пути к серверу), удерживая отмену до конца шага, иcloseего ждёт. -
Ломающее:
ctx.each(stream, (child, event) { ... })сразу возвращает дочернююJob<void>. Ждите.value, если нужен результат, который бросает, смотрите.doneили отменяйте подписку отдельно через.cancel(). Колбэк получаетSoloContext<S, W>, который сохраняет рабочий тип иkeepWhileродителя и не наследуетcanStart. Родитель ждёт этого ребёнка даже без явногоawait, и наблюдатели видят ребёнка. Отмена родителя идёт к нему каскадом. -
Отмена
eachсразу снимает подписку, а затем ждёт идущий колбэк, прежде чем ребёнок завершится и уберёт за собой. Чтобы отвечать на отмену, пользуйтесь контрольными точками контекста ребёнка; обычныйawaitможет задержать отмену иclose(). Уборку источника при отмене подписки по-прежнему не ждут.eachтеперь метод контекста и следует ограничениямrunна то, когда и где могут стартовать дети. -
JobContext.onCancelпередаёт отмену тому, что умеет остановиться по-настоящему: токену отмены устройства, прерыванию HTTP-запроса.waitкончает ожидание, а не работу. -
Дочерние задачи через
ctx.run; родитель кончается после своих детей. -
SoloQueueсremove,removeWhere,clear,lastWhere; политикиsequential,droppable,replace,restart. -
externalSetStateдля слушателей устройства; каждое изменение заново проверяет правила идущих задач. -
SoloObserverи хуки экземпляра; трассировка движкаSoloBase.debug. -
Хук, который бросает, ничего не меняет: движок отдаёт его ошибку текущей зоне и идёт дальше, а хук рядом с ним всё равно вызывается.
После 0.1.0
Заголовок раздела «После 0.1.0»- Ядро задач теперь живёт в
package:async_jobи реэкспортируется целиком:package:solo/solo.dartостаётся единственным импортом. Код на старом API причин должен перейти на классы причин. Жизненный цикл задачи, контекст, исходы и наблюдатель переехали в ядро; состояние, очередь и правила остаются вsolo. CancelReasonстал расширяемой иерархией классов:ManualCancelReason,ParentCancelReasonиHandlerCancelReasonиз ядра плюсRulesCancelReasonиClosedCancelReasonизsolo. Проверяйте типы;nameслужит только меткой для журнала, равенства по имени нет. Свои подклассы могут нести любые данные черезjob.cancel(reason: reason). Передача от родителя и от ребёнка сохраняет исходную отмену вcause.Cancelled.by({reason, started, description, stackTrace})публичный: движок предметной области строит отмены с собственной причиной.JobContext<S, W>переименован вSoloContext<S, W>, а имяJobContextтеперь принадлежит интерфейсу ядра: отмена, семейство ожиданий и дети, без состояния.SoloContextреализует его и добавляетstate,stateAsиemit. Тела задач этого имени не пишут, поэтому код, написанный по README, собирается как раньше.- Задача и её контекст разделены по шву, по которому ядро будет вынуто:
JobBase<T>иJobContextBaseнесут жизненный цикл, семейство ожиданий, детей и исход, а подклассыsoloдобавляют состояние, правила и очередь. В публичном API контроллера ничего не сдвигается. - Список детей, которых ждёт задача, теперь сокращается по мере их завершения:
долгоживущая задача, которая запускает детей в цикле, больше не растёт
на запись за ребёнка. Описание собственного исхода родителя не изменилось:
связь исхода с ребёнком, который его принёс, живёт в
Expando, поэтому ребёнок без ключа в нём по-прежнему виден. - Значение, которое тело вернуло после того, как его отмена уже пришла,
освобождается, а не отбрасывается: тело регистрирует его в стеке уборки,
исходом остаётся отмена, и
closeждёт освобождения. - Задача говорит, кто может её принять:
ctx.run(child)отказывает задаче другого контроллера сArgumentError, а задаче, которая ещё ждёт в очереди, соStateError, и задачеsoloотказывает контекст голого ядра. Ребёнок без собственного наблюдателя наследует наблюдателя родителя. JobBase.debugтрассирует жизнь задачи,SoloBase.debugтрассирует очередь, состояние и закрытие. Задайте оба, чтобы следить за обоими.JobObserverслужит наблюдателем одной задачи:onStart,onFinish,onErrorиonLog, без контроллера в сигнатурах. Контроллер кормит из него свойSoloObserverи свои хуки экземпляра, поэтому для пользователяsoloничего не меняется.isQueuedпереезжает изJob<T>в новыйSoloJob<T>, который возвращаютjob,addиrun. Очередь принадлежит контроллеру, а не задаче.JobStatusпубличный и имеет три значения: задача в очереди остаётсяcreated, пока не стартует, аisQueuedвычисляется по самой очереди: внутриcanStartзадача, взятая из очереди, в очереди уже не стоит.
Не публиковалась.