Впровадження методів Promise в JS
Привіт, прекрасні люди в Інтернеті.
API Promise
в JavaScript є основою для роботи з асинхронним кодом. Його корисність проявляється в таких методах, як Promise.all
, Promise.allSettled
, Promise.any
і Promise.race
. Кожен метод вирішує конкретні сценарії, що робить асинхронне програмування зручнішим і виразнішим. У цій статті ми розглянемо їх реалізацію, варіанти використання та особливі випадки.
Promise.all
Огляд
Promise.all
виконується, коли всі обіцянки в масиві будуть виконані.
Впровадження методів Promise в JS
Якщо будь-яка обіцянка відхилена, весь метод буде негайно відхилений.
Кастомна реалізація
PromiseAll = function(promises) {
const result = []; // Для зберігання значень, отриманих від обіцянок
let totalResolved = 0; // Для відслідковування кількості виконаних обіцянок
return new Promise((resolve, reject) => {
// Якщо вхідний масив порожній, одразу вирішуємо з порожнім масивом
if (promises.length === 0) return resolve(result);
// Проходимо по кожній обіцянці в масиві
promises.forEach((promise, index) => {
// Переконуємося, що поточне значення обробляється як обіцянка
Promise.resolve(promise).then(res => {
result[index] = res; // Зберігаємо значення обіцянки на відповідному індексі
totalResolved++; // Збільшуємо лічильник виконаних обіцянок
// Якщо всі обіцянки виконані, вирішуємо основну обіцянку з результатами
if (totalResolved === promises.length) {
return resolve(result);
}
}).catch(err => {
// Якщо будь-яка обіцянка відхилена, відхиляємо основну обіцянку негайно
return reject(err);
});
});
});
};
Приклад використання
const promise1 = Promise.resolve('A');
const promise2 = Promise.resolve('B');
const promise3 = Promise.reject('C');
PromiseAll([promise1, promise2, promise3]).then(res => {
console.log(res);
}).catch(err => {
console.error(err); // Виводить 'C'
});
Граничні випадки
- Порожній масив: вирішується в порожній масив.
- Значення, що не є обіцянками (наприклад,
undefined
,null
): автоматично вирішуються.
PromiseAll([undefined, null, 'Static value']).then(res => {
console.log(res); // Виводить [undefined, null, "Static value"]
});
Promise.allSettled
Огляд
Promise.allSettled
чекає, поки всі обіцянки не завершаться, незалежно від того, чи були вони виконані чи відхилені.
Кастомна реалізація
PromiseAllSettled = function(promises) {
const result = []; // Для зберігання результатів кожної обіцянки
let totalResolved = 0; // Для відслідковування кількості завершених обіцянок
return new Promise(resolve => {
// Якщо вхідний масив порожній, одразу вирішуємо з порожнім масивом
if (promises.length === 0) return resolve(result);
// Проходимо по кожній обіцянці в масиві
promises.forEach((promise, index) => {
// Переконуємося, що поточне значення обробляється як обіцянка
Promise.resolve(promise).then(res => {
// Якщо обіцянка виконана, зберігаємо її значення і статус
result[index] = { status: 'fulfilled', value: res };
}).catch(err => {
// Якщо обіцянка відхилена, зберігаємо її причину і статус
result[index] = { status: 'rejected', reason: err };
}).finally(() => {
// Збільшуємо лічильник завершених обіцянок
totalResolved++;
// Якщо всі обіцянки завершилися, вирішуємо основну обіцянку з результатами
if (totalResolved === promises.length) {
return resolve(result);
}
});
});
});
};
Приклад використання
PromiseAllSettled([promise1, promise2, promise3]).then(res => {
console.log(res);
});
Вивід:
[
{ status: 'fulfilled', value: 'A' },
{ status: 'fulfilled', value: 'B' },
{ status: 'rejected', reason: 'C' }
]
Граничні випадки
- Порожній масив: вирішується в порожній масив.
- Значення, що не є обіцянками (наприклад,
undefined
,null
): автоматично вирішуються.
PromiseAllSettled([42, "hello", undefined, null]).then(results => {
console.log(results);
});
Вивід
[
{ status: 'fulfilled', value: 42 },
{ status: 'fulfilled', value: 'hello' },
{ status: 'fulfilled', value: undefined },
{ status: 'fulfilled', value: null }
]
Promise.any
Огляд
Promise.any
вирішується першою виконаною обіцянкою.
Якщо всі обіцянки відхилені, кидається помилка AggregateError
.
Кастомна реалізація
PromiseAny = function(promises) {
const errors = []; // Для зберігання помилок від відхилених обіцянок
let totalRejected = 0; // Для відслідковування кількості відхилених обіцянок
return new Promise((resolve, reject) => {
// Якщо вхідний масив порожній, відхиляємо з AggregateError одразу
if (promises.length === 0) return reject(new AggregateError(errors, "Empty array"));
// Проходимо по кожній обіцянці в масиві
promises.forEach((promise, index) => {
// Переконуємося, що поточне значення обробляється як обіцянка
Promise.resolve(promise).then(res => {
// Якщо обіцянка виконана, вирішуємо основну обіцянку негайно
return resolve(res);
}).catch(err => {
// Зберігаємо помилку від відхиленої обіцянки
totalRejected++;
errors[index] = err;
// Якщо всі обіцянки відхилені, відхиляємо з AggregateError
if (totalRejected === promises.length) {
return reject(new AggregateError(errors, "All promises were rejected"));
}
});
});
});
};
Приклад використання
PromiseAny([promise1, promise2, promise3]).then(res => {
console.log(res); // Виводить 'B'
}).catch(err => {
console.error(err); // Якщо всі обіцянки відхилені
});
Граничні випадки
- Порожній масив: кидає
AggregateError
.
Promise.race
Огляд
Promise.race
вирішується або відхиляється, як тільки перша обіцянка завершується.
Кастомна реалізація
PromiseRace = function(promises) {
return new Promise((resolve, reject) => {
// Якщо вхідний масив порожній, вирішуємо негайно
if (promises.length === 0) return resolve();
// Проходимо по кожній обіцянці в масиві
promises.forEach(promise => {
// Переконуємося, що поточне значення обробляється як обіцянка
Promise.resolve(promise).then(res => {
// Вирішуємо основну обіцянку, як тільки перша обіцянка буде виконана
return resolve(res);
}).catch(err => {
// Відхиляємо основну обіцянку, як тільки перша обіцянка буде відхилена
return reject(err);
});
});
});
};
Приклад використання
PromiseRace([promise1, promise2]).then(res => {
console.log(res); // Виводить 'Promise2 resolved' після 2000мс
}).catch(err => {
console.error(err); // Якщо швидша обіцянка відхилена
});
Граничні випадки
- Порожній масив: вирішується негайно.
- Значення, що не є обіцянками, обробляються як виконані негайно.
PromiseRace([null, undefined]).then(res => {
console.log(res); // Виводить 'null'
});
Висновок
Ці методи пропонують гнучкість для різних асинхронних сценаріїв. Обробляючи граничні випадки, такі як порожні масиви та значення, що не є обіцянками, ми забезпечуємо надійні реалізації, придатні для реальних застосувань.
Я завжди прагну до вдосконалення і буду радий почути ваші думки! Якщо у вас є пропозиції, відгуки або теми, які ви хотіли б, щоб я розглянув, не соромтесь поділитися ними в коментарях нижче. Ваші зауваження дуже цінні! 🙂
Перекладено з: Mastering Promise Methods in JavaScript: A Comprehensive Guide