Хорошо, я рассмотрю новое резюме вашего вопроса в ваших комментариях (вам, вероятно, следует отредактировать свой вопрос, чтобы просто сказать это):
Я хочу запустить фрагмент кода набудущая итерация цикла событий наиболее эффективным способом (и пусть текущий метод возвращается).Никаких особых предпочтений, но порядок продолжения должен иметь значение.Например, в моем примере, если свойство1 изменилось, то свойство2 изменилось, я сначала хочу, чтобы propertyChanged был запущен для свойства1, а затем для свойства2 (в обоих случаях асинхронно с кодом, который изменил оба свойства).
Короткая версия - вы можете использовать практически любой из приведенных ниже вариантов для решения вашей проблемы.Не зная больше о ваших конкретных ситуациях / требованиях, я бы, вероятно, порекомендовал setImmediate()
, потому что он не может вызвать голодание очереди событий, если сработает рекурсивно, но либо process.nextTick()
, либо Promise.resolve().then()
сработает быстрее (до событий других типов), еслиэто имеет значение для вашего абонента.
Вот некоторые объяснения каждого выбора - каждый, вероятно, будет выполнять вашу задачу, но каждый отличается в некоторых деталях.
Все эти опции позволяют текущий тик событияцикл до конца, и затем они планируют обратный вызов, который будет вызван в будущем тике цикла событий.Они отличаются в точности, когда будет вызван следующий обратный вызов, а некоторые будут отличаться, когда они планируют следующий обратный вызов в зависимости от того, какой тип события обрабатывается в настоящее время (например, когда цикл событий находится в процессе сканирования нескольких различных очередей событий).
Вы можете начать с прочтения этой обзорной статьи Цикл событий Node.js, таймеры и process.nextTick ()
process.nextTick (cb)
Это самый быстрый способ запланировать обратный вызов.Текущий тик цикла событий завершает свое выполнение, а затем, прежде чем код цикла событий node.js просматривает любые другие очереди событий в цикле событий, он ищет элементы в nextTickQueue
и запускает их.Обратите внимание, что можно «заморозить» цикл обработки событий, если вы постоянно вызываете process.nextTick()
рекурсивно, потому что он не дает другим событиям возможности запускаться до тех пор, пока nextTickQueue
не станет пустым.Это не «честный» планировщик.

setImmediate (cb)
Это расписание обратного вызовазапускаться после завершения текущей «фазы» цикла событий.Вы можете думать о цикле событий как о циклическом прохождении через ряд различных типов очередей.Когда текущий тип очереди, которая обрабатывается, пуст, то все ожидающие обратные вызовы setImmediate()
будут обработаны.
Обратите внимание, как это относится к другим типам событий, затем зависит от того, какой тип события обрабатывался при вызове setImmediate()
.
Например, если вы находились в обратном вызове завершенияс fs.read()
и вы вызвали setImmediate()
, чтобы запланировать обратный вызов, тогда цикл обработки событий сначала обработает любые другие ожидающие события ввода-вывода, а затем обработает ваш обратный вызов setImmediate()
.Поскольку он не вызывается до тех пор, пока цикл событий не перейдет к следующему типу события в очереди событий, вы не можете заморозить цикл событий с setImmediate()
.Даже рекурсивный вызов setImmediate()
все равно будет циклически проходить через все события.
То, как обрабатывается ожидающий setTimeout()
относительно setImmediate()
, который вы запланировали, зависит от того, на какой фазе цикла событий вы находились, когда вы вызывалиsetImmediate()
.Как правило, это выходит за рамки того, что вы должны знать в своем коде.Если важна относительная синхронизация нескольких асинхронных операций, подобных этой, то гораздо безопаснее просто написать код, который гарантирует заданную последовательность независимо от того, когда именно эта операция включена ее обратным вызовом.Обещания могут помочь вам упорядочить такие вещи.
setTimeout (cb, 0)
Таймеры являются одной фазой цикла событий. Поскольку он обходит цикл обработки событий, рассматривая различные типы очередей событий, одним из этапов является поиск любых событий таймера, время которых прошло, и, следовательно, пришло время вызвать их обратный вызов. Из-за этого таймеры работают только тогда, когда цикл событий находится в «фазе таймера», поэтому то, как они запускаются относительно других типов событий, является неопределенным. Это зависит от того, где находится цикл обработки событий, когда таймер готов к работе. Лично я обычно не использую setTimeout(cb, 0)
, если не пытаюсь синхронизироваться с другими событиями таймера, поскольку это гарантирует порядок FIFO с другими событиями таймера, но не с другими типами событий.
Promise.resolve (). Затем (Си-Би)
Чтобы достичь этого уровня детализации для обещаний (которые вам обычно не нужны), вы должны быть очень осведомлены о том, что реализация обещаний вы используете и как она работает. Реализация обещаний, не связанных с собственным кодом, будет использовать один из других механизмов синхронизации для планирования своих обработчиков .then()
. Любой из них может соответствующим образом соответствовать спецификации Promise, поэтому они могут различаться.
Собственные обещания в node.js имеют конкретную реализацию. Лично я не знаю причин, почему вы должны писать код, который зависит от этой конкретной реализации, но многим людям, кажется, любопытно, поэтому я объясню.
В этой статье вы можете увидеть хорошую диаграмму: Promises, nextTicks и setImmediates . Собственные обещания реализуются с использованием так называемой очереди микро-задач. По сути, это другая очередь, такая как очередь nextTick, которая обрабатывается после nextTickQueue
, но перед любой из других очередей. Таким образом, обработчики .then()
или .catch()
в очереди запускаются сразу после и nextTick
вызовов, которые уже запланированы и перед любыми другими типами событий (таймеры, завершение ввода-вывода и т. Д.).

Реализации, не относящиеся к нативным обещаниям (например, Bluebird или Q), не имеют возможности создать новую очередь микрозадач, которая обрабатывается после очереди nextTick, поэтому они используют setImmediate()
или process.nextTick()
.