Promise - специальный объект, который хранит своё состояние и используется для обработки результатов асинхронных операций.
Что будет в статье:
Основные задачи на Promsie, которые часто спрашивают на собеседованиях
Код задач с подробными комментариями
Чего в статье НЕ будет:
Теории про Promsie и Event Loop
Задач на Event Loop, показывающих приоритетность выполнения Promise, setTimeout и т.д.
Статья предназначена для тех, кто уже изучил теорию по Promise и хочет попрактиковаться в решении тестовых задач.
Основные методы Promise
Приведу список методов, которые будут использоваться а задачах:
"Promise.resolve(value)"
- создаёт успешно выполнившийся Promise с результатом value"Promise.reject(value)"
- создаёт выполнившийся Promise с ошибкой".then"
- обработчик событий Promise"onFulfilled"
и"onRejected"
".catch"
- обработчик события Promise"onRejected"
".finally"
- дает возможность запустить один раз определенный участок кода, который должен выполниться вне зависимости от того, с каким результатом выполнился Promise
Задачи на .then
1. Случаи, когда .then возвращает или не возвращает значение
Promise.resolve("1")
// Promise.resolve вернул "1"
.then(data => {
console.log(data); // => "1"
})
// .then ничего не вернул
.then(data => {
console.log(data); // => "undefined"
return "2";
})
// .then вернул "2"
.then(data => {
console.log(data); // => "2"
})
2. Второй аргумент .then
Первая задача
Promise.reject(1)
// skip
// обработать reject можно только в .catch
.then(data => {
console.log(data);
})
// второй аргумент .then вместо .catch
.then(null, data => console.log(data)) // => 1
// после обработки ошибки попадаем в .then
// => "ok"
.then(() => console.log('ok'));
Вторая задача
Promise.reject()
// используем колбэки для .then и для .catch
.then(
data => console.log('ok'), // => skip
data => console.log('error') // => "error"
)
3. null в .then
Promise.resolve("1")
// skip
.then(null)
// придут данные из Promise.resolve
.then(data => console.log(data)) // => "1"
Задачи на .catch
1. Ошибка из Promise.reject
Promise.reject("Api Error")
// skip из-за Promise.reject
.then(data => console.log('ok'))
// обработка ошибки
.catch(error => {
console.log(error); // => "Api Error"
return "1";
})
// catch вернул "1"
.then(data => {
console.log(data); // => "1"
})
2. Ошибка из Promise.reject в .catch
Promise.reject()
// обработка Promise.reject
.catch(() => {
console.log('error1'); // => "error1"
return Promise.reject();
// аналогично
// return reject();
})
// обработка Promise.reject из предыдущего .catch
.catch(() => {
console.log('error2'); // => "error2"
})
3. Ошибка throw new Error
Promise.resolve()
.then(data => {
// возникновение ошибки
throw new Error('Api Error');
// не имеет значения, что вернули
return 1;
})
// skip, потому что предыдущий .then бросил ошибку
.then(data => console.log('ok'))
// обработка ошибки
.catch(error => {
console.log(error.message); // => "Api Error"
return "2";
})
// .catch вернул "2"
.then(data => {
console.log(data); // => "2"
})
4. Необработанная ошибка в Promise
Необработанная ошибка в Promise не влияет на работу программы, т.к. Promise не выпускает ошибку за свои пределы.
Promise.resolve()
.then(() => {
throw new Error('Api Error');
})
// код будет работать
setTimeout(() => {
console.log('setTimeout'); // => "setTimeout"
}, 1000);
5. null в .catch
Promise.reject("Api Error")
// skip: ошибку не обработали
.catch(null)
// skip из-за необработанной ошибки
.then(data => console.log('ok'))
// обработка ошибки
.catch(error => console.log(error)) // => "Api Error"
// .then выполнится
.then(data => console.log('ok')) // => "ok"
Задачи на .finally
Первая задача
Promise.resolve()
.then(() => {
return "1";
})
// .then вернул "1", но .finally пропустит его мимо себя
.finally(data => {
console.log(data); // => "undefined"
return "2";
})
// из .finally вернули "2", но результат берется из предыдущего .then или .catch
.then(data => console.log(data)) // => "1"
Вторая задача
Promise.reject()
// .finally выполняется в любом случае: даже при возникновении ошибки
.finally(data => {
console.log('finally'); // => "finally"
})
Задачи на несколько Promise
Если несколько Promise, то
".then"
будет выполняться последовательно для каждого:
Promise.resolve()
.then(() => console.log(1)) // "Первый"
.then(() => console.log(2)) // "Третий"
Promise.resolve()
.then(() => console.log(11)) // "Второй"
.then(() => console.log(12)) // "Четвертый"
При
".catch"
аналогично:
Promise.resolve()
.then(() => console.log(1)) // "Первый"
.then(() => { console.log(2); throw new Error(); }) // "Третий"
.catch(() => console.log(3)) // "Пятый"
.then(() => console.log(4)) // "Седьмой"
Promise.resolve()
.then(() => console.log(11)) // "Второй"
.then(() => { console.log(12); throw new Error(); }) // "Четвертый"
.catch(() => console.log(13)) // "Шестой"
.then(() => console.log(14)) // "Восьмой"
Задача на бесконечные микротаски
В качестве бонуса покажу, как повесить браузер микротасками, которыми являются Promise:
const foo = () => {
Promise.resolve().then(() => foo())
}
foo();
Заключение
В статье я собрал базовые задачи с собеседований. Чтобы понять Promise, необходимо изучить теорию и приступать к практике. Стоит практиковать задачи в различных вариациях: расширять задачи, изменять существующие примеры и отслеживать изменения.
На этом всё. И да прибудет с нами сила!
Комментарии (17)
Alexandroppolus
12.10.2021 10:30+2с собеса, несложная:
const p = Promise.reject(); p.then(() => console.log('ok')); p.catch(() => console.log('error'));
Объяснить, почему выскакивает "UnhandledPromiseRejection", хотя хотя вроде бы навесили catch.
anton-sergeenkov Автор
12.10.2021 10:46+1Вообще, у меня ошибка не возникла, но предположу, потому что здесь нет чейнинга
Создали промис
Обработали только then - тут нет catch и будет ошибка
-
Обработали только .catch отдельно
Если так переписать, то будет корректно
Promise.reject() .then(() => console.log('ok')) .catch(() => console.log('error'))
Suvitruf
12.10.2021 12:38Так это же разные примеры. То, что вы написали — это не то же самое, что написал Alexandroppolus.
Когда вы навешивает then'ы после создания промиса, то они не выстраивают одну цепочку, а будут выполняться параллельно.
aamonster
12.10.2021 11:18+1Красиво, пришлось думать. Не сразу допёр "сосчитать" промисы, попадающие в очередь (в смысле что p.then() – это новый промис).
aamonster
12.10.2021 16:30Upd: Забавно, похоже все, кроме меня, мыслят чейнами, а не отдельными промисами. Надо подумать, какое восприятие проще (видимо, чейны) и нет ли в мышлении чейнами каких-то подводных камней.
Alexandroppolus
12.10.2021 16:45+1Надо мыслить отдельными чейнами )
Еррор выскакивает только на тех отклоненных промисах, которые последние в цепочке (т.е. для которого ни разу не вызвали then или catch). В моем примере поздно было ловить ошибку на первом промисе - она уехала во второй.
вот, например, в таком раскладе:
const p = Promise.reject(); p.then(() => console.log('ok')); p.then(() => console.log('ok'));
выскочит 2 "Unhandled", потому что 2 конечных промиса, хотя всего 3 отклоненных.
Suvitruf
12.10.2021 13:14Потому что все новые чейны на уже созданный промис навешиваются, а не в одну цепочку.
У вас:p.then() p.catch()
Не связаны друг с другом и выполняются параллельно.
Если нужен один чейн, то либо в одну цепочку вешайте:const p = Promise.reject(); p.then(() => console.log('ok')) .catch(() => console.log('error'));
Либо сразу при создании, как anton-sergeenkov написал.
korsetlr473
12.10.2021 17:25".then"
- обработчик событий PromiseonRejected"
".catch"
- обработчик события Promise"onRejected"
чего?
anton-sergeenkov Автор
12.10.2021 17:50у .then 2 аргумента: первый обрабатывает "onFulfilled", а второй "onRejected" - таким образом, .then можно использовать как замена .catch
.catch, в свою очередь, обрабатывает только "onRejected"
noodles
15.10.2021 21:45Кстати, важный момент, если использовать формат .then(resolveHandler, rejectHandler), то rejectHandler не поймает ошибку, возникшую внутри resolveHandler в текщуем then-е.
Или в следующем then-е её ловить, или проще и безопаснее всегда ловить всё в catch.
Fodin
13.10.2021 23:07+1Очень хотел такой материал и обрадовался, когда заголовок увидел. Но был обескуражен (ноль иронии), когда не нашел задач. Задача - это "что надо сделать" (условие) и "как это сделать" (ответ, решение). А тут, получается, примеры кода с пояснениями. В таком виде оно все тоже ценно, но обещаны-то задачи. Эх. Может, есть где-то именно задачник на промисы и async/await, который проведет по всем нюансам?
anatoly314
Только лучше все таки Promise не пользоваться по возможности. Где встречаю, стараюсь переписать на async/await.
anton-sergeenkov Автор
На собеседованиях в основном по Промисам гоняют =)
aamonster
Ну, это и есть промисы, просто в более удобном и понятном виде)
hardovsky
А ничего что async/await просто синтаксический сахар над Promise?
anatoly314
Я прекрасно это знаю, и использование этого syntactical sugar позволяет избежать многих ошибок как например в примере выше.
qrKot
для того, чтобы пользоваться syntactic (не syntactical, тактического в нем ничего нет) sugar, очень полезно знать, поверх чего он работает... Тем более любая асинхронная функция таки возвращает Promise.