Узел; Q Задержка обещания

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

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

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

> "THING 2"
> "THING 1"

Вопросы:

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

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

3) Могут ли быть проблемы производительности с ценностями, которые глубоко обернуты в обещаниях? Например, делает 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+, The then Раздел метода, пункт 4:

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

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

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

Так что это на самом деле предписано спецификацией.

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

Другие вопросы по тегам