Узел; 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. Поскольку реализация обещания считается кодом платформы, она сама может содержать очередь планирования задач или "батут", в котором вызываются обработчики.
Так что это на самом деле предписано спецификацией.
Это было широко обсуждено, чтобы убедиться, что это требование ясно - см.: