Класста Promise
6 статикалык метод бар. Келгиле, алар менен таанышалы.
Promise.all
Көптөгөн убадаларды параллелдүү жүргүзүп, алардын баары аткарылганга чейин күтүшүбүз керек дейли.
Мисалы, бир нече файлды параллелдүү жүктөңүз жана натыйжа даяр болгондон кийин аны иштетиңиз.
Бул үчүн, ал пайдалуу болот Promise.all
.
Синтаксис:
@A@let promise = Promise.all(iterable);@A@
Метод Promise.all
көптөгөн убадаларды алат (ал ар кандай кайталануучу объектти ала алат, бирок массив адатта колдонулат) жана жаңы убаданы кайтарат.
Жаңы убада убадалардын бардык өткөн тизмеси аяктагандан кийин бүтөт жана алардын натыйжаларынын бир катар натыйжасын берет.
Мисалы, Promise.all
төмөнкү 3 секунддан кийин аткарылат жана массивге алып келет [1, 2, 3]
:
@A@Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3
]).then(alert); // когда все промисы выполнятся, результат будет 1,2,3
// каждый промис даёт элемент массива@A@
Массив элементтеринин тартиби баштапкы убадалардын тартибине так дал келерин эске алыңыз. Биринчи убада эң узакка созулса дагы, анын натыйжасы массивде биринчи болуп калат.
Көбүнчө колдонулган амал - бул ар бир элемент үчүн убада тапшырмасын түзө турган карта функциясы аркылуу берилиштер массивин өткөрүп, андан кийин пайда болгон массивди Promise.all
.
Мисалы, бизде шилтемелердин массивдери бар болсо, анда биз аларды төмөнкүдөй жүктөй алабыз:
@A@let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://api.github.com/users/jeresig'
];
// Преобразуем каждый URL в промис, возвращённый fetch
let requests = urls.map(url => fetch(url));
// Promise.all будет ожидать выполнения всех промисов
Promise.all(requests)
.then(responses => responses.forEach(
response => alert(`${response.url}: ${response.status}`)
));@A@
Бул жерде GitHub колдонуучулары жөнүндө маалыматты массивден логиндери боюнча алуу менен чоңураак мисал (биз алардын идентификаторлору боюнча өнүмдөрдүн массивдерин ала алабыз, логика бирдей):
@A@let names = ['iliakan', 'remy', 'jeresig'];
let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));
Promise.all(requests)
.then(responses => {
// все промисы успешно завершены
for(let response of responses) {
alert(`${response.url}: ${response.status}`); // покажет 200 для каждой ссылки
}
return responses;
})
// преобразовать массив ответов response в response.json(),
// чтобы прочитать содержимое каждого
.then(responses => Promise.all(responses.map(r => r.json())))
// все JSON-ответы обработаны, users - массив с результатами
.then(users => users.forEach(user => alert(user.name)));@A@
Эгерде убадалардын бири ишке ашпай калса, анда кайтарылган убада Promise.all
ошол ката менен дароо ишке ашпай калат.
Мисалы:
@A@Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ошибка!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(alert); // Error: Ошибка!@A@
Бул жерде экинчи убада 2 секунддан кийин ишке ашпай калат. Бул дароо катага алып келет Promise.all
, демек, .catch
бул убаданын катасы бүтүндөй ката болуп калат Promise.all
.
Бир убада ишке ашпай калса, Promise.all
тизмедеги калган убадаларды таптакыр унутуп, бүт убада ишке ашпай калат. Алардын натыйжалары эске алынбайт.
fetch
Мисалы, жогорудагы мисалдагыдай бир нече чалуулар болуп , бирөө өтпөй калса, калгандары аткарыла берет, бирок Promise.all
аларга мындан ары каралбайт. Кыязы, алар тигил же бул жол менен бүтөт, бирок алардын натыйжалары көңүл бурулбайт.
Promise.all
аларды жокко чыгаруу үчүн эч нерсе кылбайт, анткени убадаларда «жокко чыгаруу» деген түшүнүк таптакыр жок. Алып алуу: Өтүнүчтү токтотуу бөлүмүндө биз Fetch'ти карайбыз AbortController
, ал буга жардам берет, бирок бул Promise API'нин бир бөлүгү эмес.
Promise.all(iterable)
убада бербегендерге өтүүгө мүмкүндүк берет iterable
(кайталануучу объект)Адатта, Promise.all(...)
ал кайталануучу убада объектисин алат (көбүнчө массив). Бирок, эгерде бул объекттердин кайсынысы бир убада болбосо, анда ал акыркы массивге "кандай болсо, ошондой" өтөт.
Мисалы, бул жерде жыйынтык:[1, 2, 3]
@A@Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000)
}),
2,
3
]).then(alert); // 1, 2, 3@A@
Ошентип, биз убада кылынбаган даяр баалуулуктарды өткөрүп алабыз, Promise.all
кээде бул ыңгайлуу.
Promise.allSettled
Синтаксис:
@A@let promise = Promise.allSettled(iterable);@A@
Promise.all
эгерде ал берилген убадалардын биринде болсо, ишке ашпай калат. Бул улантуу үчүн бардык натыйжаларга муктаж болгон бардык же эч нерсе болгон жагдайлар үчүн ылайыктуу :
@A@Promise.all([
fetch('/template.html'),
fetch('/style.css'),
fetch('/data.json')
]).then(render); // методу render нужны результаты всех fetch
Метод Promise.allSettled
ар дайым бардык убадалардын аткарылышын күтөт. Жыйынтык массив болот
{status:"fulfilled", value:результат}
ийгиликтүү аяктоо үчүн{status:"rejected", reason:ошибка}
каталар үчүн.@A@
Мисалы, биз колдонуучулардын топтому жөнүндө маалыматты жүктөөнү каалайбыз. Кээ бир суроо-талапта ката болсо дагы, калгандарына кызыкдарбыз.
Бул үчүн биз колдонобуз Promise.allSettled
:
@A@let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://no-such-url'
];
Promise.allSettled(urls.map(url => fetch(url)))
.then(results => { // (*)
results.forEach((result, num) => {
if (result.status == "fulfilled") {
alert(`${urls[num]}: ${result.value.status}`);
}
if (result.status == "rejected") {
alert(`${urls[num]}: ${result.reason}`);
}
});
});
results
Саптагы массив (*)
мындай болот:
[
{status: 'fulfilled', value: ...объект ответа...},
{status: 'fulfilled', value: ...объект ответа...},
{status: 'rejected', reason: ...объект ошибки...}
]@A@
Башкача айтканда, ар бир убада үчүн бизде анын статусу жана мааниси/катасы бар.
Polyfill
Эгер браузер колдоого албаса Promise.allSettled
, аны көп толтуруу оңой:
@A@if(!Promise.allSettled) {
Promise.allSettled = function(promises) {
return Promise.all(promises.map(p => Promise.resolve(p).then(value => ({
status: 'fulfilled',
value: value
}), error => ({
status: 'rejected',
reason: error
}))));
};
}@A@
Бул коддо promises.map
ал аргументтерди алып, аларды убадаларга айлантат (болбосо эле) жана ар бирине иштеткичти кошот .then
.
Бул иштеткич ийгиликтүү жыйынтыкты value
га {state:'fulfilled', value: value}
, катаны error
ге айлантат {state:'rejected', reason: error}
. Бул так жыйынтыктардын форматы Promise.allSettled
.
Андан кийин кээ бирлери ишке ашпай калса дагы, бардыкPromise.allSettled
убадалардын натыйжасын алуу үчүн колдоно алабыз .
Promise.race
Метод абдан окшош Promise.all
, бирок биринчи аткарылган убаданы гана күтөт, андан натыйжа (же ката) алынат.
Синтаксис:
@A@let promise = Promise.race(iterable);@A@
Мисалы, бул жерде натыйжа болот 1
:
@A@Promise.race([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ошибка!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1@A@
Биринчи убада эң тез аткарылып, натыйжасын берди. Андан кийин башка убадаларга көңүл бурулбайт.
Promise.any
Метод абдан окшош , бирок биринчи ийгиликтүүPromise.race
убаданы гана күтөт , андан натыйжа чыгат.
AggregateError
Эгерде өткөн убадалардын бири да ишке ашпаса, анда кайтарылган Убада объекти бардык убада каталарын сактаган атайын ката объектиси менен четке кагылат errors
.
Синтаксис:
@A@let promise = Promise.any(iterable);@A@
Мисалы, бул жерде, натыйжасы болот 1
:
@APromise.any([
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ошибка!")), 1000)),
new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1@A@
Бул мисалдагы биринчи убада эң тез болгон, бирок ал четке кагылгандыктан, натыйжа экинчиси болгон. «Жарышта жеңет» деген биринчи ийгиликтүү убададан кийин, мындан аркы бардык жыйынтыктар этибарга алынбайт.
Бул жерде бардык убадалар четке кагылган мисал:
@A@Promise.any([
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ошибка!")), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ещё одна ошибка!")), 2000))
]).catch(error => {
console.log(error.constructor.name); // AggregateError
console.log(error.errors[0]); // Error: Ошибка!
console.log(error.errors[1]); // Error: Ещё одна ошибка!
});@A@
Көрүнүп тургандай, четке кагылган убадалар үчүн ката объекттери errors
объект касиетинде жеткиликтүү AggregateError
.
Promise.resolve/reject
Promise.resolve
жана ыкмалары Promise.reject
заманбап коддо сейрек колдонулат, анткени синтаксис ( бир аз кийинчерээкasync/await
карайбыз ) аларды жалпысынан керексиз кылат.
Биз бул жерде аларды толуктоо үчүн, ошондой эле кандайдыр бир себептерден улам колдоно албагандар үчүн чагылдырабыз async/await
.
Promise.resolve
Promise.resolve(value)
натыйжасы менен ийгиликтүү убада жарататvalue
.
Ошол эле:
@A@let promise = new Promise(resolve => resolve(value));@A@
Бул ыкма шайкештик үчүн колдонулат: функция убаданы так кайтарат деп күтүлгөндө.
Мисалы, төмөндөгү функция loadCached
URL жүктөйт жана анын мазмунун эстейт (кэш). Ошол эле URL менен болочок чалууларда, ал дароо кэштен мурунку мазмунду окуйт, бирок аны Promise.resolve
убада кылуу үчүн колдонот, ошондуктан кайтаруу мааниси ар дайым убада болот:
@A@let cache = new Map();
function loadCached(url) {
if (cache.has(url)) {
return Promise.resolve(cache.get(url)); // (*)
}
return fetch(url)
.then(response => response.text())
.then(text => {
cache.set(url,text);
return text;
});
}@A@
Биз жаза алабыз, loadCached(url).then(…)
анткени функция loadCached
ар дайым убаданы кайтарат. .then
Биз ар дайым кийин колдоно алабыз loadCached
. Promise.resolve
Бул сапты колдонуунун максаты (*)
.
Убададан баш тартуу
Promise.reject(error)
менен ишке ашпай турган убаданы жарататerror
.
Ошол эле:
let promise = new Promise((resolve, reject) => reject(error));
Иш жүзүндө бул ыкма дээрлик колдонулбайт.
Бардыгы
Биз алты статикалык класс ыкмаларын көрдүк Promise
:
Promise.all(promises)
- бардык убадалардын аткарылышын күтөт жана натыйжалары менен массивди кайтарат. Көрсөтүлгөн убадалардын кайсынысы катаны кайтарса, анда иштин жыйынтыгыPromise.all
ушул ката болуп калат, башка убадалардын натыйжалары этибарга алынбайт.Promise.allSettled(promises)
(жакында кошулган) - бардык убадалар аяктаганга чейин күтөт жана алардын натыйжаларын объекттери менен массив катары кайтарат, ар бир объекттин эки касиети бар:status
:"fulfilled"
ийгиликтүү болсо же"rejected"
ката болсо,value
– ийгиликтүү болсо натыйжа жеreason
болбосо – ката.
Promise.race(promises)
- биринчи аткарылган убаданы күтөт , ал анын натыйжасы болуп калат, калгандарына көңүл бурулбайт.Promise.any(promises)
(жакында кошулган) - биринчи ийгиликтүү убада анын натыйжасы болушун күтөт, калгандарына көңүл бурулбайт. Эгерде бардык берилген убадалар четке кагылса,AggregateError
бул ката болуп калатPromise.any
.Promise.resolve(value)
– натыйжасы менен ийгиликтүү аяктаган убаданы кайтаратvalue
.Promise.reject(error)
– ката менен убаданы кайтаратerror
.
Сандалган бардык ыкмалардын ичинен эң көп колдонулганы балким Promise.all
.