Что определяет порядок вызова отложенной функции с использованием промисов или setTimeout?

Отсрочка выполнения функций, например, при обработке пользовательских событий, является распространенным шаблоном в JavaScript (см., например, здесь). Раньше единственным способом сделать это было использование setTimeout(myFunc,0), однако с обещаниями теперь есть альтернатива: Promise.resolve().then(myFunc).

Я предполагал, что они будут делать примерно одно и то же, но, работая над библиотекой, включающей пользовательские события, я подумал, что узнаю, есть ли разница, поэтому я поместил следующий блок в узел:

var logfn=function(v){return function(){console.log(v)}};

setTimeout(logfn(1),0);
Promise.resolve().then(logfn(2));
logfn(3)();

Я ожидал увидеть на консоли 3, 1, 2, но вместо этого я увидел 3, 2, 1. Другими словами, обещание НЕ эквивалентно использованию setTimeout и сначала выходит из блоков. По крайней мере, в Node.

Я повторил тест в Chrome и Firefox с тем же результатом, однако в Edge он получился как 3, 1, 2. Я также ожидаю, что неродные библиотеки промисов будут использовать setTimeout под капотом, поэтому результат будет таким же, как у Edge.

Что определяет порядок разрешения этих вызовов? Какая модель используется этими различными средами для определения порядка выполнения? Представляет ли что-либо из вышеперечисленного стандартное или нестандартное поведение?

PS Я определенно не предлагаю полагаться на что-либо из этого, чтобы оставаться последовательным, мне просто любопытно.


После того, как приведенный ниже ответ указал мне правильное направление, и, как кратко упомянуто в комментарии ниже, я нашел полный ответ в отличном статья Джейка Арчибальда (с примером, почти идентичным моему коду выше), который я бы добавил сюда, а не оставил в комментарии.


person Euan Smith    schedule 20.04.2016    source источник
comment
FWIW, ничто не гарантирует какой-либо конкретный порядок асинхронного выполнения. Вы никогда не должны пытаться полагаться на порядок выполнения в первую очередь. Что делает этот вопрос интересным обсуждением деталей реализации, но практически спорным.   -  person deceze♦    schedule 20.04.2016
comment
Может быть, но я склонен считать, что мой код улучшается, чем больше я понимаю, как все это связано, даже если это косвенно. Покопавшись в ответе ниже, я также нашел почти точно мой пример выше плюс полное объяснение здесь. Учитывая все это, я определенно вижу случаи, когда может быть уместным нажатие на очередь микрозадач или макрозадач, хотя помня о том, что вам нужно убедиться, что ничего не сломано, когда доступны только макрозадачи.   -  person Euan Smith    schedule 20.04.2016


Ответы (1)


ll зависит от того, как resolve() был реализован внутри - возможно, вы заметили разницу между реализациями setTimeout(fn, 0) и Edge для setImmediate(fn)

Пожалуйста, обратите внимание на статью - http://www.mattgreer.org/articles/promises-in-wicked-detail/ и способ реализации метода resolve.

function resolve(value) {
    // force callback to be called in the next
    // iteration of the event loop, giving
    // callback a chance to be set by then()
    setTimeout(function() {
        callback(value);
    }, 1);
}

Некоторые пояснения можно найти в приоритете между setTimeout и setImmediate

Из документации Microsoft — https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/dev-guide/performance/efficient-script-yielding/ и еще одна ссылка - метод setImmediate

person Krzysztof Safjanowski    schedule 20.04.2016
comment
Спасибо за ссылку на статью и ссылку на setImmediate, которая была для меня новой. Просмотр этого привел меня к полифиллу setImmediate, который привел к этому предыдущему ответу stackoverflow о разнице между < href="http://stackoverflow.com/a/25933985/4098951">очередь событий микрозадач и макрозадач, которая, кажется, объясняет, что происходит. Я предполагаю, что Chrome и другие используют очередь событий микрозадач для реализации обещаний с setTimeout, идущим в очередь макрозадач. Эдж, возможно, не делает такого различия. - person Euan Smith; 20.04.2016