Блог Михаила Крамера. PHP и JS

Почему важно уметь работать с промисами, а не только с async/await

Опыт руководства командой молодых фронтенд разработчиков показал, что сейчас при обучении уделяется недостаточно внимание "классу" Promise, а делается ставка на синтаксический сахар async/await. Это не всегда себя оправдывает. Небольшое отступление: во многих источниках, даже на MDN, пишут "объект Promise", но с точки зрения теории ООП, это скорее класс. Но настоящих классов в JS не бывает (даже в новом стандарте это сахар для прототипов), поэтому слово класс я беру в кавычки

Нельзя забывать, что async/await - это способ превратить асинхронные вызовы в "синхронные" - async-функция выполняется асинхронно, но, все await-вызовы внутри будут работать как синхронные, т.е. один за другим. Когда это создаёт неудобства? Когда надо сделать много независимых вызовов на сервер. Предположим мы все их сделаем так:

const f = async () => {
	let a = await callServerA();
	let b = await callServerB();
	let c = await callServerC();
}

Что мы получили в итоге? пока не закончился один серверный вызов, не произойдёт другой. А ведь они могут уйти на сервер одновременно, некоторые браузеры позволяют делать до 9 постоянных подключений. А в новых версиях протокола и того больше. Зачем же заставлять наших пользователей ждать дольше, чем нужно?

И тут на сцену выходит "класс" Promise и его статический метод all. Всё очень просто:

const f = () => {
	Promise.all([
		callServerA()
		callServerB()
		callServerC()
	])
}

Теперь все вызовы уйдут на сервер почти одновременно. Но давайте разовьём мысль. А что если нам надо сделать что-то с результатами всех трёх вызовов. Предположим, все они возвращают числа, и нам надо их сложить и записать в элемент с id result. Никаких проблем:

const f = () => {
	Promise.all([
		callServerA()
		callServerB()
		callServerC()
	]).then(([resp1, resp2, resp3]) => {
		document.getElementById("result").innerText = resp1 + resp2 + resp3;
	});
}

А если нам надо поймать и обработать неудачу любого из промисов? Тоже не проблема:

const f = () => {
	Promise.all([
		callServerA()
		callServerB()
		callServerC()
	]).cacth(() => console.log("ERROR"))
}

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

const f = () => {
	Promise.all([
		Promise.all([
			callServerA(),
			callServerE().then(() => { /* Do something with E result */})
		])
		callServerB()
		callServerC()
	]).finally(() => console.log("All the requests are done"));
}

Общий вывод: async/await - очень удобный синтаксический сахар, но иногда нужна настоящая асинхронность, и тогда лучше использовать методы "класса" Promise

Ваш комментарий
Комментарии