Почему конструктору Promise требуется функция, которая вызывает "разрешение" по завершении, но "then" - нет - вместо этого возвращается значение?
Как я окунулся в изучение Promise
s, мое понимание остановилось на следующем вопросе, который я не нахожу обсуждаемым (все, что я нахожу, - это конкретные обсуждения конструктора Promise
, а Promise
'then
', но не обсуждение, которое сравнивает их шаблоны проектирования).
1. Конструктор Promise
Из документации MDN, , мы используем конструктор Promise (добавлен мой комментарий):
new Promise(function(resolve, reject) { ... }); // <-- Call this Stage 1
Объект функции с двумя аргументами resolve
и reject
. Первый аргумент исполняет обещание, второй аргумент отвергает его. Мы можем вызовите эти функции после завершения нашей работы.
2. Функция then
Переходя к функции then
, которую можно вызвать в объекте Promise
(который возвращает новый объект Promise
), мы следующая сигнатура функции, описанная в документации (с добавленными мной комментариями):
p.then(onFulfilled, onRejected);
Chaining
Поскольку метод then
возвращает Promise, вы можете легко вызовы.
var p2 = new Promise(function(resolve, reject) {
resolve(1); // <-- Stage 1 again
});
p2.then(function(value) {
console.log(value); // 1
return value + 1; // <-- Call this Stage 2
}).then(function(value) {
console.log(value); // 2
});
Мой вопрос
Из приведенного выше фрагмента кода мне кажется, что значение передано функции resolve
в 1-й стадии (во втором входе resolve
- ниже (2), выше ) передается на следующий этап (первая функция then
, следующая в том же фрагменте кода). На этапе 1 нет возвращаемого значения. Однако это возвращаемое значение на этапе 2, которое после этого переходит на следующий этап (вторая функция then
).
Это отсутствие соответствия между шаблоном проектирования для создания Promise
и использование функции then
по существующему обещанию (которое также возвращает Promise
), просто исторический случай (один требует вызова обратного вызова, но ничего не возвращает, а другой возвращает значение, но не вызывает обратный вызов)?
Или мне не хватает основной причины, почему конструктор Promise
использует другой шаблон проектирования, чем функция then
?
Ответы
Ответ 1
Ответ Берги превосходный, и был очень полезен для меня. Этот ответ дополняет его. Чтобы визуализировать связь между конструктором Promise()
и then()
, я создал эту диаграмму. Надеюсь, это поможет кому-то... может быть, даже мне, через несколько месяцев.
Основная идея здесь заключается в том, что функция "исполнителя", переданная конструктору Promise()
, устанавливает задачи в движении, которые будут устанавливать состояние обещания; тогда как обработчики, которые вы передадите на then()
, будут реагировать на состояние обещания.
(Примеры кода, адаптированные из классический учебник Jake Archibald.)
Это очень упрощенное представление о том, как все работает, оставляя много важных деталей. Но я думаю, что если вы сможете сохранить хороший обзор намеченной цели, это поможет избежать путаницы, когда вы попадете в детали.
Несколько выбранных деталей
Исполнитель немедленно вызывается
Одна важная деталь заключается в том, что функция-исполнитель, переданная конструктору Promise()
, называется немедленно (до того, как конструктор вернет обещание); тогда как функции обработчика, переданные методу then()
, не будут вызываться до позже (если вообще).
Берги упомянул об этом, но я хотел повторить его, не используя термины а/синхронно, что можно смутить, если вы не читаете внимательно: различие между функцией, вызывающей нечто асинхронно или вызываемой асинхронно, легко замаскировать в общении.
resolve()
не onFulfill()
Еще одна деталь, которую я хотел бы подчеркнуть, потому что это немного смутило меня в том, что обратные вызовы resolve()
и reject()
, переданные функции-исполнителю конструктора Promise()
, не обратные вызовы позже передаются методу then()
. Это кажется очевидным в ретроспективе, но явная связь заставила меня крутиться в кругах слишком долго. Существует определенно соединение, но оно свободно, динамично.
Вместо этого обратные вызовы resolve()
и reject()
представляют собой функции , предоставляемые системой, и передаются функции-исполнителю конструктором Promise
при создании обещания. Когда вызывается функция resolve()
, выполняется системный код, который потенциально изменяет состояние обещания и в конечном итоге приводит к обратному вызову onFulfilled()
, который вызывается асинхронно. Не думайте призывать resolve()
как плотную оболочку для вызова onFulfill()
!
Ответ 2
Нет никакого соответствия между конструктором Promise
и then
, потому что это две независимые вещи, предназначенные для разных целей.
Конструктор Promise
используется только для promisifying 1 асинхронных функций. Действительно, как вы говорите, он построен на вызове resolve
/reject
обратных вызовов для асинхронной отправки значений, и в этом случае нет возвращаемых значений.
То, что сам конструктор Promise
принимает этот "обратный" обратный вызов (которому он синхронно проходит resolve
и reject
), на самом деле является улучшением старшего отложенного шаблона и не имеет никакого предполагаемого сходства для обратных вызовов then
.
var p = new Promise(function(res, rej) { | var def = Promise.Deferred();
setTimeout(res, 100); | setTimeout(def.resolve, 100);
}); | var p = def.promise;
Обратные вызовы then
в контрасте - это классические асинхронные обратные вызовы с дополнительной функцией, из которых вы можете return
от них. Для получения значений они асинхронно вызываются.
p.then(function(val) { … });
Подводя итог различиям:
-
Promise
является конструктором, а then
является методом
-
Promise
принимает один обратный вызов, а then
занимает до двух
-
Promise
синхронно вызывает обратный вызов, а then
асинхронно вызывает свои обратные вызовы
-
Promise
всегда вызывает обратный вызов,
then
может не вызывать свои обратные вызовы (если обещание не выполнено/отклонено)
-
Promise
передает возможности для разрешения/отклонения обещания обратного вызова,
then
передает значение результата/отклонения результата обещания, которое оно вызывало на
-
Promise
вызывает обратный вызов с целью выполнения побочных эффектов (вызов reject
/resolve
),
then
вызывает свои обратные вызовы для их значений результата (для цепочки)
Да, оба возвращают promises, хотя они разделяют эту черту со многими другими функциями (Promise.resolve
, Promise.reject
, fetch
,...). Фактически все они основаны на тех же принципах построения обещаний и разрешения/отклонения, которые также предоставляет конструктор Promise
, хотя это и не их основная цель. then
в основном предлагает возможность прикреплять обратные вызовы onFulfilled
/onRejected
к существующему обещанию, которое довольно диаметрально для конструктора Promise
.
То, что оба используют обратные вызовы, просто совпадение - не историческая случайность, а скорее коадаптация языковой функции.
1: В идеале вам никогда не понадобится это, потому что все асинхронные API-интерфейсы возвращаются promises
Ответ 3
Весь смысл функции-исполнителя-конструктора обещаний заключается в распространении функций разрешения и отклонения на использование кода без обещаний, его обертывании и преобразовании для использования обещания. Если вы хотите ограничить это только синхронными функциями, то да, вместо этого можно было бы использовать возвращаемое значение из функции, но это было бы глупо, поскольку полезная часть - распространять распознаватель и отклонять функции на код, который на самом деле выполняется позже (путь после возврата), например для обратных вызовов, переданных в некоторый асинхронный API.
Ответ 4
Вдохновленный предыдущими ответами (я рассмотрю ту часть, которая меня больше всего сбила с толку):
Аргументы resolve
и reject
в конструкторе Promise не являются функциями, которые вы определяете. Подумайте о них как о крючках, которые вы можете внедрить в свой код операции async (обычно вы resolve
с ответом на успех и reject
с причиной сбоя), так что javascript имеет возможность в конечном итоге маркировать обещание как выполненное или отклоненное в зависимости от результата вашего асинхронная работа; как только это произойдет, соответствующая функция, которую вы определили then(fun1, fun2)
, запускается, чтобы потреблять обещание (либо fun1(success_response)
либо fun2(failure_reason)
, в зависимости от того, выполнено ли обещание/отклонено). Поскольку fun1
и fun2
- это простые старые javascript-функции (они просто принимают будущие результаты вашей операции async как аргументы), они return
значения (которые могут быть undefined
если вы явно не возвращаете).
Также см. Замечательные статьи Mozilla:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise