Promise что за программа

Доброго времени суток, Хабр! Представляю вашему вниманию перевод статьи «Understanding Promises in JavaScript» автора Sukhjinder Arora.

От автора перевода: Так же, как и сам автор, я надеюсь, что статья оказалась для вас полезной. Пожалуйста, если она и вправду помогла вам узнать для себя что-то новое, то не поленитесь зайти на оригинал статьи и поблагодарить автора! Буду рад вашему фидбеку!

JavaScript — это однопоточный язык программирования, это означает, что за раз может быть выполнено что-то одно. До ES6 мы использовали обратные вызовы, чтобы управлять асинхронными задачами, такими как сетевой запрос.

Используя промисы, мы можем избегать “ад обратных вызовов” и сделать наш код чище, более читабельным и более простым для понимания.

Предположим, что мы хотим асинхронно получить некоторые данные с сервера, используя обратные вызовы мы сделали бы что-то вроде этого:

ES6 #12. JavaScript Promise. Что это и как работает (+ запросы данных из Coffee API, Beer API и др.)


getData(function(x)< console.log(x); getMoreData(x, function(y)< console.log(y); getSomeMoreData(y, function(z)< console.log(z); >); >); >);

Здесь я запрашиваю некоторые данные с сервера при помощи функции getData(), которая получает данные внутри функции обратного вызова. Внутри функции обратного вызова я запрашиваю дополнительные данные при помощи вызова функции getMoreData(), передавая предыдущие данные как аргумент и так далее.

Это то, что мы называем “адом обратных вызовов”, где каждый обратный вызов вложен внутрь другого, и каждый внутренний обратный вызов зависит от его родителя.

Что такое Promise в JS

Что такое Promise в JS

Promise появились в стандарте ES6. В стандарте ES5 функциональность промисов уже можно было использовать, только вынесена она была в отдельные библиотеки. Теперь же промисы официально добавлены в стандарт и если вы все еще не работали с промисами и не знаете что это такое, самое время в этом разобраться.

Предистория

Любой код написанный на JavaScript выполняется синхронно (последовательно сверху вниз). Однако после того, как скрипты стали усложняться и для того чтобы не блокировать дальнейшее выполнение программы, стал активнее использоваться асинхронный код. Для того чтобы задать последовательность для асинхронных операций, придумали промисы.

До появления промисов для этой задачи использовали коллбэки. Однако из-за возникновения больших вложенностей у этих callback функций, написание кода превратилось в ад. Возникла острая потребность в более элегантном синтаксисе, так и появились Promise.

Объект Promise (обещание) позволяет более удобно работать с асинхронным кодом. В синхронном коде, строки кода выполняются одна за другой. В асинхронном коде, дело обстоит немного иначе. Например у нас есть изображение, но нужно, чтобы оно загрузилось не одновременно с веб-страничкой, а при нажатии на кнопку.

Читайте также:
Samsung voice recorder что это за программа и нужна ли она

Урок 5. JavaScript. Promise. Что это, как работает (+ пример)

Синхронный код

Что произойдет в синхронном коде, когда пользователь нажмет на кнопку? Браузер полностью заблокируется и не будет реагировать на другие действия пользователя. Все то время пока изображение грузится, пользователь не сможет делать что-то еще на сайте.

Асинхронный код

В асинхронном коде не блокируются какие-то другие действия пользователя. Можно спокойно выполнять такие операции, которые требуют некоторого ожидания, отложенности во времени, и в то же время, не тормозя другие процессы на странице.

Работа с Promise

В обычном программировании, когда все строки кода выполняются последовательно и без ожидания завершения какой-то загрузки, Promise совершенно не нужен. Он применяется в достаточно сложных вещах, что-то вроде технологии AJAX. Сейчас рассмотрим на абстрактном примере принципы работы Promise — опишем функцию setTimeout(), которая будет имитировать выполнение длительного ожидания.

Асинхронное программирование с промисами JavaScript

Асинхронное программирование с промисами JavaScript

Концепция асинхронного кода может показаться довольно сложной, особенно начинающим разработчикам. К счастью, промисы JavaScript существенно облегчают работу с такого рода ситуациями.

Skillfactory.ru

“Управление сложностью — квинтэссенция программирования”, — Брайн Керниган.

Как сказал Брайн: суть программирования состоит в способности разработчиков справляться со сложными ситуациями. Для достижения профессионального мастерства мы должны смело осваивать трудные концепции, подобно обозначенной ранее.

В данной статье рассмотрим понятие промисов и их назначение. Кроме того, выясним, почему так важно связывать промисы в цепочки и как метод Promise.all() помогает объединять несколько промисов в один.

Вперед за знаниями!

Что такое промис?

Промис — объект JavaScript, который занимается обработкой асинхронного кода. Они позволяют откладывать блоки кода до момента выполнения или отклонения действия. Вместо передачи обратных вызовов в функцию они прикрепляются к концу промиса, обеспечивая лучшую читаемость и возможность применения цепочек.

Не следует их путать с async/await .

Для полного понимания принципов действия промисов сначала рассмотрим пример с обратными вызовами, а затем перейдем к обзору других случаев.

const DEFAULT_TIMEOUT = 500;

function getMovieTicket(movie,fulfillMovieTicket,rejectMovieTicket) setTimeout(() => if(movie.payment >= movie.ticket_price) fulfillMovieTicket(`Success! Payment has been processed.`);
>else rejectMovieTicket(`Error: Payment less than ticket price.`);
>
>,300)
>

function selectMovie(selection,selectedMovie,rejectMovieSelection) setTimeout(() => if(selection.time_taken <= DEFAULT_TIMEOUT)selectedMovie(selection.title)
>else rejectMovieSelection(`Your session has expired.`)
>
>,DEFAULT_TIMEOUT)
>

const movie_obj = payment: 21.25,
ticket_price: 21.00,
title: ‘Adventures of Pickle Rick’,
time_taken: 200
>

selectMovie(movie_obj,(movie) => console.log(`Movie selected: $`)
getMovieTicket(movie_obj,(response) => console.log(response);
>,(err) => console.error(err);
>)
>,(err) => console.error(err);
>)

// Вывод: фильм выбран — «Приключения огурчика Рика»
// Успешно! Оплата прошла.

Перед вами пример киоска по продаже билетов в кино, который использует обратные вызовы для обработки асинхронного кода. И selectMovie() , и getMovieTicket() передают 2 обратных вызова в качестве аргументов. Первая функция вызывается в случае успешной транзакции, а вторая — неудачной.

Читайте также:
Microsoft sql что это за программа

Поскольку функции selectMovie() и getMovieTicket() являются асинхронными, обратные вызовы используются, чтобы отложить выполнение других строк кода до момента завершения процесса.

Для корректной работы этого примера необходимо убедиться в правильном порядке выполнения рассмотренных функций во избежание ошибочного результата, поскольку при взаимодействии с асинхронным кодом такие случаи вовсе не редкость.

Перепишем вышеуказанный пример с помощью промисов и посмотрим, что изменилось:

const DEFAULT_TIMEOUT = 500;

const getMovieTicket = (movie) =>
new Promise((resolve,reject) => setTimeout(() => if(movie.payment >= movie.ticket_price) resolve(`Success! Payment has been processed.`);
>else reject(`Error: Payment less than ticket price.`);
>
>,300)
>);

const selectMovie = (selection) =>
new Promise((resolve,reject) => setTimeout(() => if(selection.time_taken <= DEFAULT_TIMEOUT)resolve(selection.title)
>else reject(`Your session has expired.`)
>
>,DEFAULT_TIMEOUT)
>);

const movie_obj = payment: 21.25,
ticket_price: 21.00,
title: ‘Adventures of Pickle Rick’,
time_taken: 200
>

selectMovie(movie_obj).then(movie => console.log(`Movie selected: $`)
getMovieTicket(movie_obj).then(response => console.log(response);
>).catch(err => console.error(err);
>)
>).catch(err => console.error(err);
>)

// Вывод: фильм выбран — «Приключения огурчика Рика»
// Успешно! Оплата прошла.

Как видно, данная реализация ненамного отличается от предыдущей. Вместо передачи обратных вызовов в функции мы возвращаем промис, что позволяет прикрепить обратные вызовы к концу промиса посредством методов then() и catch() .

Важно отметить, что промисы запускаются синхронно и становятся асинхронными только внутри методов then() и catch() .

При разделении двух асинхронных методов изменится вывод. Дело в том, что ни один из промисов не ждет выполнения другого, поскольку теперь они независимы друг от друга. В этом и заключается разница между промисами и async/await в JavaScript.

getMovieTicket(movie_obj).then(response => console.log(response);
>).catch(err => console.error(err);
>)

selectMovie(movie_obj).then(movie => console.log(`Movie selected: $`)
>).catch(err => console.error(err);
>)

// Вывод: успешно! Оплата прошла.
// Фильм выбран — «Приключения огурчика Рика»

Рассмотрим подробнее, как работают эти два метода.

Promise.then()

Метод then() возвращает промис. При его успешном выполнении then() вызывает функцию, которая возвращает полученное значение. В данном случае им является значение, передаваемое в обратный вызов resolve() в ранее изученном примере.

Promise.catch()

Аналогично then() метод catch() также возвращает промис, но только в случае его отклонения. Возможно, он вам известен по опыту использования блока try-catch . Если промис отклоняется, catch() вызывает функцию, которая возвращает причину возникшей ошибки.

Посмотрим на Promise.catch() в действии:

const movie_obj = payment: 19.00,
ticket_price: 21.00,
title: ‘Adventures of Pickle Rick’,
time_taken: 200
>

selectMovie(movie_obj).then(movie => console.log(`Movie selected: $`)
getMovieTicket(movie_obj).then(response => console.log(response);
>).catch(err => console.error(err);
>)
>).catch(err => console.error(err);
>)

Читайте также:
Тренировочные программы это что

// Вывод: фильм выбран — «Приключения огурчика Рика»
// Ошибка: оплата меньше стоимости билета.

Поскольку оплата меньше стоимости билета, причина отклонения промиса передается в обратный вызов rejected() , что приводит к вышеуказанному результату.

А что если нужно вывести квитанцию по факту совершенной оплаты?

Посмотрим, как это сделать посредством объединения промисов в цепочки.

Объединение промисов в цепочки

Суть процедуры проста. Если нужно записать в лог квитанцию о совершении оплаты, просто прикрепляем один метод then() к концу предыдущего.

Рассмотрим код ниже и посмотрим, как это сработает:

selectMovie(movie_obj).then(movie => console.log(`Movie selected: $`)
getMovieTicket(movie_obj).then(response => console.log(response);
return response
>).then(details => const receipt = ‘Keep this as your receipt | ‘ + details
console.log(receipt);
>)
.catch(err => console.error(err);
>)
>).catch(err => console.error(err);
>)

// Вывод: фильм выбран — «Приключения огурчика Рика»
// Успешно! Оплата прошла.
// Сохраните квитанцию об оплате. Успешно! Оплата прошла.

Для корректной работы кода необходимо убедиться, что мы возвращаем значение из первого then() , иначе второй метод вернет undefined . Вы можете связывать друг с другом желаемое число методов.

Хотя данный код работает корректно, в реальных ситуациях вывод квитанции тоже будет асинхронным действием. В связи с этим следует поместить его в отдельный промис следующим образом:

const printReceipt = (payment_details, status) =>
new Promise((resolve,reject) => setTimeout(() => if(status === ‘OK’) resolve(‘Keep this as your receipt | ‘ + payment_details)
>else reject(‘Error: The printer has encountered a jam.’)
>
>,300)
>)

В данном фрагменте кода мы проверяем, завершает ли принтер работу в состоянии OK перед выполнением промиса. Если он сталкивается с ошибкой, отправляем отклоненный промис и записываем ошибку в консоль.

Если добавить данный промис к остальным, получим вот такой результат:

selectMovie(movie_obj).then(movie => console.log(`Movie selected: $`)
getMovieTicket(movie_obj).then(response => console.log(response);
printReceipt(response,’OK’).then(receipt => console.log(receipt);
>).catch(err => console.error(err);
>)
>)
.catch(err => console.error(err);
>)
>).catch(err => console.error(err);
>)

// Вывод: фильм выбран — «Приключения огурчика Рика»
// Успешно! Оплата прошла.
// Сохраните квитанцию об оплате | Успешно! Оплата прошла.

С точки зрения читаемости данный вариант выглядит хуже.

Для подобной ситуации есть особое название — “ад обратных вызовов”. Это происходит, когда несколько промисов выполняются друг за другом, чтобы завершить процесс асинхронной обработки.

Исправить это можно путем объединения промисов в цепочки!

Именно такое решение помогает эффективно справиться с подобными ситуациями. Убедимся на следующем примере:

selectMovie(movie_obj).then(movie => console.log(`Movie selected: $`)
return getMovieTicket(movie_obj);
>)
.then(response => console.log(response);
return printReceipt(response,’OK’)
>)
.then(receipt => console.log(receipt)
>)
.catch(err => console.error(err);
>)

// Вывод: фильм выбран — «Приключения огурчика Рика»
// Успешно! Оплата прошла.
// Сохраните квитанцию об оплате | Успешно! Оплата прошла.

Так намного лучше!

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru