Node; Q Обещание

Вот несколько простых вопросов, основанных на поведении, которое я заметил в следующем примере, запущенном в node:

Q('THING 1').then(console.log.bind(console));
console.log('THING 2');

Выход для этого:

> "THING 2"
> "THING 1"

Вопросы:

1) Почему Q реализован для ожидания перед запуском обратного вызова по значению, которое сразу же известно? Почему не Q достаточно умный, чтобы первая строка могла синхронно выдавать свой вывод до того, как будет запущена вторая строка?

2) Каков промежуток времени между выводами "THING 2" и "THING 1"? Является ли это одним тиком процесса?

3) Могут ли быть проблемы с производительностью со значениями, которые глубоко завернуты в promises? Например, Q(Q(Q("THING 1"))) асинхронно ждет в 3 раза больше времени, даже если он может быть эффективно синхронно разрешен?

Ответ 1

Это делается специально. Это необходимо для того, чтобы он был согласован независимо от того, известно ли значение или нет. Таким образом, существует только один порядок оценки, и вы можете зависеть от того, что независимо от того, уже обещано или нет обещание, этот порядок будет таким же.

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

Это довольно похоже на код обратного вызова:

function fun(args, callback) {

    if (!args) {
        process.nextTick(callback, 'error');
    }
    // ...
}

чтобы любой, кто его называет:

fun(x, function (err) {
  // A
});
// B

может быть уверен, что A никогда не будет работать до B.

Спектр

См. Promises/A + Спецификация, then Способ, пункт 4:

onFulfilled или onRejected не нужно вызывать, пока стек контекста выполнения не содержит только код платформы.

См. также примечание 1:

Здесь "код платформы" означает код реализации, среды и обещания. На практике это требование гарантирует, что onFulfilled и onRejected выполняются асинхронно, после того, как цикл цикла обработки событий, который затем вызывается, и со свежим стекем. Это может быть реализовано либо с помощью механизма макрозадачи, такого как setTimeout или setImmediate, либо с помощью механизма "микрозадачи", такого как MutationObserver или process.nextTick. Поскольку реализация обещаний считается кодом платформы, она может сама содержать очередь планирования задач или "батут", в котором вызываются обработчики.

Итак, это действительно задано спецификацией.

Было подробно обсуждено, чтобы убедиться, что это требование ясное - см.: