Как работает then в промисах?

Ссылка скопирована
1 ответ

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...

Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Лучший ответ
1
Анна SEO Ответ

Для понимания работы метода 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);
});

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 и позволяет писать более понятный и структурированный код.

Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно