Использование обещаний | Promise

Promise – это специальный объект, который содержит своё состояние. Вначале pending («ожидание»), затем – одно из: fulfilled («выполнено успешно») или rejected («выполнено с ошибкой»). Используется для организации асинхронного кода - т.е. не сидим и не ждём завершения рдействия а выподняем что-то ещё.

var promise = new Promise(function(resolve, reject) {
  // Эта функция будет вызвана автоматически

  // В ней можно делать любые асинхронные операции,
  // А когда они завершатся — нужно вызвать одно из:
  // resolve(результат) при успешном выполнении
  // reject(ошибка) при ошибке
})

resolve - выполняется в случае успеха, reject - в случае неудачи.

После создания promise для его использования - нужно навесить обработчики. Навешивание обработчиков происходит через ключевое слово then.

promise.then(onFulfilled, onRejected)
  • onFulfilled – функция, которая будет вызвана с результатом при resolve.
  • onRejected – функция, которая будет вызвана с ошибкой при reject.
// Только обработчик успеха
promise.then(function(details) {
    // handle success
});
// Только обработчик отказа
promise.then(null, function(error) {
    // handle failure
});
// Обработчики успеха и отказа
promise.then(
    function(details) { /* handle success */ },
    function(error) { /* handle failure */ }
);

Для того, чтобы поставить обработчик только на ошибку, вместо .then(null, onRejected) можно написать .catch(onRejected) – это то же самое.

promise.catch(function(){})

Промисификация

Промисификация – это когда берут асинхронный функционал и делают для него обёртку, возвращающую промис. После промисификации использование функционала зачастую становится гораздо удобнее.

function httpGet() {
	return new Promise(function(resolve, reject) {
		setTimeout(() => {
			let data = {id:1, name: 'John'};
			resolve(data);
		}, 300);
	});
}
httpGet().then( data => console.log(data) ); // Object { id: 1, name: "John" }	

Цепочки промисов

Каждый раз когда используется then - создаётся новое обещание и передаётся дальше по цепочке. Поэтому промисы удобно организовывать в цепочки. Для всей цепочки можно указать один обработчик .catch(onRejected).

let promise = new Promise(function(resolve, reject) {
	setTimeout(() => {
		Math.random() > .5 ? resolve({id: 1, name: 'John'}) : reject('Error');
	}, 300);
}); 
promise
	.then(function(data){
		console.log('step 1');
	}) 
	.then(function(data){
		console.log('step 2');
	}) 
	.catch(data => console.error(data))
	.then(function(data){
		console.info('promise finish');
	})
;
// step 1
// step 2
// promise finish

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

let promise = new Promise(function(resolve, reject) {
	console.log('promise start');
	setTimeout(() => {
		resolve({id: 1, name: 'John'});
	}, 300);
}); 
promise
	.then(data => {
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				console.log('step 1');
				data.email = 'john@gmail.com';
				resolve(data);
			}, 300);
		});
	})
	.then(data => {
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				console.log('step 2');
				data.phone = '8-55-33-77';
				resolve(data);
			}, 300);
		});
	})
	.then(function(data){
		console.info('promise finish');
		console.info(data);
	});
// promise start
// step 1
// step 2
// promise finish
// Object { id: 1, name: "John", email: "john@gmail.com", phone: "8-55-33-77" }

Методы Promise.resolve(value) и Promise.reject(error) - позволяют моментально выполнить и отклонить обещания.

Параллельное выполнение

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

function go(num){
	return new Promise(function(resolve, reject){
		let delay = Math.ceil(Math.random() * 3000);
		console.log('nom =', num, 'delay =',delay);
		// setTimeout( () => resolve(num), delay );
		setTimeout( () => {
			if(delay > 2000){
				reject(num);
			}
			else{
				resolve(num);
			}
		}, delay );
	});
}

let p1 = go(1);
let p2 = go(2);
let p3 = go(3);

Promise.all([p3, p2, p1])
	.then(value => console.log('value=', value))
	.catch(error => console.warn(error))
	;
// nom = 1 delay = 703
// nom = 2 delay = 705
// nom = 3 delay = 582
// value= Array [ 3, 2, 1 ]

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

function go(num){
	return new Promise(function(resolve, reject){
		let delay = Math.ceil(Math.random() * 3000);
		console.log('nom =', num, 'delay =',delay);
		setTimeout( () => resolve(num), delay );
	});
}

let p1 = go(1);
let p2 = go(2);
let p3 = go(3);

Promise.race([p3, p2, p1])
	.then(value => console.log('value =', value))
	.catch(error => console.warn(error))
	;
// nom = 1 delay = 534
// nom = 2 delay = 1145
// nom = 3 delay = 427
// value = 3

Подробнее: