Как работает then в промисах?
P.S. Вопрос может быть и похож на дубликат, но все же немного о другом.
Чисто визуально я вижу эффект как работает промис и как он передает результат ошибку в then. И вроде могу даже написать как надо. Однако не понимаю фразу "then тоже возвращает промис", а если точнее - не понимаю, как именно это происходит. Попробую объяснить на примерах, что именно не понятно.
Возьмем вот такой промис:
let prom = new Promise((resolve, reject) => { resolve(5); }); |
let prom = new Promise((resolve, reject) => { resolve(5); });
► Ситуация 1: есть обработчик
prom .then(result => { console.log(result); }); |
prom .then(result => { console.log(result); });
Чисто визуально тут все как будто очевидно - в resolve мы передали результат 5, он сохранился в промисе. А тут мы передали промису функцию, которой этот результат нужен. Он эту функцию вызывает, передает ей 5 и на этом дело заканчивается. Здесь можно даже и не заподозрить, что then что-то возвращает.
► Ситуация 2: нет обработчика
prom .then(null) .then(result => { console.log(result); }); |
prom .then(null) .then(result => { console.log(result); });
Вот тут уже интереснее. Появляется цепочка из then и становится очевидно, что then возвращает промис, т.к. на результате мы снова можем вызвать then. Но поскольку там null, можно подумать, что исходный промис просто как-то передается дальше.
► Ситуация 3: обработчик возвращает простое значение
prom .then(result => { return 10; // Вот это простое значение }) .then((result) => { console.log(result); }); |
prom .then(result => { return 10; // Вот это простое значение }) .then((result) => { console.log(result); });
Еще интереснее - обработчик возвращает простое значение, которое моментально попадает в следующий then.
► Ситуация 4: обработчик возвращает промис
prom .then(result => { return new Promise((resolve, reject) => { // Вот этот промис setTimeout(() => resolve(20), 2000); }); }) .then(result => { console.log(result); }); |
prom .then(result => { return new Promise((resolve, reject) => { // Вот этот промис setTimeout(() => resolve(20), 2000); }); }) .then(result => { console.log(result); });
Обработчик возвращает промис, результат которого через две секунды попадает в следующий обработчик.
► Ситуация 5, дополнительная: случай с ошибкой
prom .then(result => { throw new Error("Ошибочка вышла!"); }) .then(null, result => { console.log(result.message); }); |
prom .then(result => { throw new Error("Ошибочка вышла!"); }) .then(null, result => { console.log(result.message); });
Можете рассказать в общих чертах, если then возвращает промис, то как он его формирует? Вот на примерах этих ситуаций.
Дополнительно:
Здесь можно даже и не заподозрить, что then что-то возвращает.
Все функции в js что-то возвращают. Если явного return нет или в нём не указано значение, то возвращается undefined.
Но поскольку там null, можно подумать, что исходный промис просто как-то передается дальше.
Так и есть - вы не передали никакой callback и весь этот вызов как бы игнорируется. Этот сценарий аналогичен предыдущему.
Еще интереснее - then возвращает простое значение, которое моментально попадает в следующий then.
Это ваш callback возвращает простое значение, которое then оборачивает в отрезолвленный промис.
Можете рассказать в общих чертах, если then возвращает промис, то как он его формирует?
Ну берёт и формирует... Примерно так:
then = (onFulfilledCallback, onRejectedCallback) => { try { let newValue; if (this.previousValue instanceof Error) { newValue = onRejectedCallback(this.previousValue); } else { newValue = onFulfilledCallback(this.previousValue); } if (newValue instanceof Promise) { return newValue; } else { return Promise.resolve(newValue); } } catch (error) { return Promise.reject(error); } } |
then = (onFulfilledCallback, onRejectedCallback) => { try { let newValue; if (this.previousValue instanceof Error) { newValue = onRejectedCallback(this.previousValue); } else { newValue = onFulfilledCallback(this.previousValue); } if (newValue instanceof Promise) { return newValue; } else { return Promise.resolve(newValue); } } catch (error) { return Promise.reject(error); } }
Это псеводокод но общий смысл такой.
- Т.е. в ситуации 2 идет именно просто игнор, а не создание какого-то промиса-заглушки-пустышки, правильно я понял?
В ситуации 3 да, не then возвращает, а моя функция, выразился плохо, поправлю вопрос. А как это примерно выглядит? then выполняет мою функцию, получает результат и потом возвращает что-то вроде
new Promise((resolve, reject) => resolve(значение_из_моей_функции);
new Promise((resolve, reject) => resolve(значение_из_моей_функции);
Так получается?
А в 4 случае как тогда? then каким-то образом анализирует тип результата и если видит, что это промис, то не создает обертку, а возвращает как есть?
-
Т.е. в ситуации 2 идет именно просто игнор, а не создание какого-то промиса-заглушки-пустышки, правильно я понял?
Чтобы достоверно ответить на этот вопрос, нужно читать спеку или исходники реализации. Поскольку сценарий высосан из пальца, в реальности не встречается и ответ не имеет никакого практического значения, делать я этого не буду :)
Так получается?
Я показал псевдокодом, как это происходит в моём понимании. Опять же - разработчику не нужно погружаться в эти детали реализации, особенно в начале пути. Вас должен интересовать только внешний API, с которым вы непосредственно взаимодействуете, а он хорошо описан в документации и в примерно миллионе статей и курсов.
Желание разобраться до последнего нюанса само по себе похвально, но время и наше внимание ограничены, поэтому нужно направлять их на то, что принесёт больше пользы. Я вот не знаю деталей реализации промисов (хотя когда-то давно костылил их на C++) и прекрасно решаю любые задачи, которые передо мной появляются. Понадобится - прочту спеку. -
Т.е. в ситуации 2 идет именно просто игнор, а не создание какого-то промиса-заглушки-пустышки, правильно я понял?
Всё есть в документации.
If it is not a function, it is internally replaced with an identity function ((x) => x) which simply passes the fulfillment value forward.
- Алексей Уколов, просто я пока писал комментарий, вы уже дополнили ответ по 4 ситуации. Ковыряться в деталях реализации я не собирался, это правда не особо нужно. В целом я теперь понял, что к чему. Мне не хватало вот именно этой детали - что берется результат колбэка и оборачивается в резолвленный промис (ну или если ошибка, то в отклоненный).
Насчет того, что в статьях и курсах хорошо написано, это спорно. Например, после чтения статьи того же Ильи Кантора по промисам, у меня сформировалось впечатление, что передаваемые в then функции промис использует в качестве resolve и reject. Из-за этого я не мог понять, а как тогда получается, что промис уже может быть сто лет как выполнен к моменту вызова then. Понял только после хабра, что resolve и reject - это внутренние промисовские функции, которые он нам дает чтобы сохранить результат ошибку в сам промис. Так что хотя в целом Кантор и хабр хорош и я по его сайту изучаю, но иногда формулировки путают или недостаточные и без форумов не обойтись.
-
просто я пока писал комментарий, вы уже дополнили ответ по 4 ситуации
Ну да, я для ясности псевдокод усложнил. На самом деле Promise.resolve() вполне себе умеет принимать на вход другой промис и в дополнительной проверке нет смысла.
RTFM. Там всё описано.
https://tc39.es/ecma262/multipage/control-abstract...
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос
Для понимания работы метода then в промисах необходимо сначала разобраться в том, что такое промисы и зачем они используются в JavaScript.
Промисы - это специальный объект, который представляет собой асинхронную операцию и ее результат. Они используются для работы с асинхронным кодом, таким как запросы к серверу, чтение файлов и другие операции, которые могут занять некоторое время.
Метод then в промисах используется для обработки успешного завершения промиса. Он принимает два аргумента: функцию обратного вызова для обработки успешного выполнения промиса и функцию обратного вызова для обработки ошибки, если она возникла.
Пример использования метода then:
const promise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Success!'); }, 2000); }); promise.then((result) => { console.log(result); // Выведет 'Success!' }).catch((error) => { console.error(error); });
В данном примере создается промис, который через две секунды успешно завершается с результатом 'Success!'. Затем метод then вызывает функцию обратного вызова, которая выводит результат в консоль.
Если в процессе выполнения промиса возникнет ошибка, то будет вызвана функция обратного вызова, переданная методу catch.
Таким образом, метод then позволяет обрабатывать успешное выполнение промиса и передавать результат дальше для последующей обработки данных. Он является основным инструментом работы с асинхронным кодом в JavaScript и позволяет писать более понятный и структурированный код.