Loading...

Promise API

Promise API

 

Класста Promise6 статикалык метод бар. Келгиле, алар менен таанышалы.

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@

Бул ыкма шайкештик үчүн колдонулат: функция убаданы так кайтарат деп күтүлгөндө.

Мисалы, төмөндөгү функция loadCachedURL жүктөйт жана анын мазмунун эстейт (кэш). Ошол эле 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Биз ар дайым кийин колдоно алабыз loadCachedPromise.resolveБул сапты колдонуунун максаты (*).

Убададан баш тартуу

  • Promise.reject(error)менен ишке ашпай турган убаданы жаратат error.

Ошол эле:

let promise = new Promise((resolve, reject) => reject(error));

Иш жүзүндө бул ыкма дээрлик колдонулбайт.

Бардыгы

Биз алты статикалык класс ыкмаларын көрдүк Promise:

  1. Promise.all(promises)- бардык убадалардын аткарылышын күтөт жана натыйжалары менен массивди кайтарат. Көрсөтүлгөн убадалардын кайсынысы катаны кайтарса, анда иштин жыйынтыгы Promise.allушул ката болуп калат, башка убадалардын натыйжалары этибарга алынбайт.
  2. Promise.allSettled(promises)(жакында кошулган) - бардык убадалар аяктаганга чейин күтөт жана алардын натыйжаларын объекттери менен массив катары кайтарат, ар бир объекттин эки касиети бар:
    • status"fulfilled"ийгиликтүү болсо же "rejected"ката болсо,
    • value– ийгиликтүү болсо натыйжа же reasonболбосо – ката.
  3. Promise.race(promises)- биринчи аткарылган убаданы күтөт , ал анын натыйжасы болуп калат, калгандарына көңүл бурулбайт.
  4. Promise.any(promises)(жакында кошулган) - биринчи ийгиликтүү убада анын натыйжасы болушун күтөт, калгандарына көңүл бурулбайт. Эгерде бардык берилген убадалар четке кагылса, AggregateErrorбул ката болуп калат Promise.any.
  5. Promise.resolve(value)– натыйжасы менен ийгиликтүү аяктаган убаданы кайтарат value.
  6. Promise.reject(error)– ката менен убаданы кайтарат error.

Сандалган бардык ыкмалардын ичинен эң көп колдонулганы балким Promise.all.