Хост-середовище як джерело подій

В браузері вбудовано багато подій, наприклад mousemove - рух миші. Щоб додати функцію-підписник на певну подію, використовують метод addEventListener:

var i = 1;
window.addEventListener("mousemove", (event) => {console.log(i++)});

Вбудовані асинхронні методи типу fetch aбо setTimout, також генерують свого роду події: “надійшов результат запиту”, або “пройшло n мілісекунд”. Важливою деталлю є те що функція-підписник не виконується одразу при настанні події, а лише поміщується в чергу задач. В залежності від завантаженості черги на той момент, може бути додаткова затримка між постановкою в чергу і власне виконанням:

// тут setTiemout гарантує що функція fn буде виконана 
// НЕ РАНІШЕ ніж через 1 секунду, але можливо трохи пізніше 
setTimeout(
	function fn() {
		console.log(
			'Я виконаюсь не раніше ніж через 1 секунду (але можу і пізніше)!'
		);
	},
	1000
);

Promise: constructor

Повернемось до промісів. Конструктор проміса примає один аргумент - функцію-ініціалізатор. Ця функція виконується одразу при створенні проміса, і в свою чергу очікує в якості аргументів дві функції - одну для переводу проміса в стан fulfilled, а іншу - для переводу в rejected. Наприклад, створимо функцію-аналог setTimout, яка повертатиме проміс (до речі, цей процес називається “промісифікацією” функції):

function runWithTimeout(fn, miliseconds) {
	// Створюємо і повертаємо проміс	
	return new Promise(
		function(resolve, reject) {
			// В ініціалізаторі маємо два шляхи: викликати resolve() або reject(),
			setTimeout(
				function () {
					try {
						// Викликаємо що треба через задану кількість часу 
						var result = fn();
						// І "резолвимо" проміс з отриманим результатом
						resolve(result)
					} catch(err) {
						// А у випадку помилки - "реджектимо" проміс з обʼєктом помилки
						reject(err);
					}
				},
				miliseconds
			)
		}
	)
}

Тепер, уявимо як її можна було б використовувати:

// Виведемо чотири літери, кожну через секунду після попередньої 
runWithTimeout(
	() => console.log('A'),
  1000
).then(
	() => runWithTimeout(
		() => console.log('B'),
	  1000
	)
).then(
	() => runWithTimeout(
		() => console.log('C'),
		1000
	)
).then(
	() => runWithTimeout(
		() => console.log('D'),
	  1000
	)
);

Уявіть як це виглядало би без промісів:

// Це жах!
setTimeout(
	() => {
		console.log('A');
		setTimeout(
			() => {
				console.log('B');
				setTimeout(
					() => {
						console.log('C');
						setTimeout(
							() => {
								console.log('D');
							},
							1000
						);
					},
					1000
				);
			},
			1000
		);
	},
	1000
);

Promise: then, catch