Loading...

промистер

промистер

 

Элестетиңиз, сиз атактуу ырчысыз, ал күйөрмандардын алдыдагы сингл тууралуу суроолору менен тынымсыз кыжаалат болуп турат.

Тыныгуу үчүн, сингл чыкканда аларга жөнөтүүгө убада бересиз. Сиз күйөрмандарга катталуу үчүн тизме бересиз. Алар ыр чыккандан кийин дароо алуу үчүн электрондук почтасын ошол жерге калтырса болот. Жана андан да көп: эгер бир нерсе туура эмес болуп кетсе, мисалы, студияда өрт чыгып, ыр чыкпай калса, алар дагы бул тууралуу билдирүү алышат.

Баары бактылуу! Сиз бактылуусуз, анткени күйөрмандар мындан ары сизди убара кылбайт жана күйөрмандар жаңы синглди өткөрүп жиберем деп тынчсызданбашы керек.

Бул программалоодо биз көп жолуккан жагдайлар үчүн чыныгы жашоо окшоштугу:

  1. Убакытты талап кылган нерсени жасаган "түзүү" код бар. Мисалы, тармак аркылуу маалыматтарды жүктөө. Биздин аналогияда бул “ырчы”.
  2. "Жаратуучу" коддун натыйжасын ал даяр болгондо алгысы келген "керектөөчү" код бар. Ал бир нече функцияга керек болушу мүмкүн. Булар күйөрмандар.
  3. Promise(англисче promise, биз мындай объектти "убада" деп атайбыз) JavaScript'те "түзүү" жана "керектөө" коддорун бириктирген атайын объект. Биздин аналогия боюнча, бул "жазылуу үчүн тизме". "Түзүү" коду натыйжаны алуу үчүн канча убакыт талап кылынса, ошончо иштей алат, ал эми убада натыйжа даяр болгондо ага жазылган код үчүн натыйжаны жеткиликтүү кылат.

PromiseАналогия толугу менен так эмес, анткени JavaScriptдеги объект жазылуулардын жөнөкөй тизмесине караганда алда канча татаал: анын кошумча мүмкүнчүлүктөрү жана чектөөлөрү бар. Бирок башталгычтар үчүн бул окшоштук жакшы.

Түзүү синтаксиси Promise:

@A@let promise = new Promise(function(resolve, reject) {
  // функция-исполнитель (executor)
  // "певец"
});@A@

Конструкцияга берилген функция аткаруучуnew Promise деп аталат . Түзүлгөндө , ал автоматтык түрдө башталат. Ал акыры натыйжа бере турган "түзүү" кодун камтышы керек. Биздин аналогия боюнча: аткаруучу – “ырчы”.Promise

Анын аргументтери resolveжана rejectJavaScript өзү тарабынан берилген кайра чалуулар. Биздин код аткаруучунун ичинде гана.

Ал жыйынтыкты алганда, азыр же кийинчерээк, бул маанилүү эмес, бул кайра чалуулардын бирин чакырышы керек:

  • resolve(value)— эгерде иш ийги-ликтуу аяктаган болсо, натыйжа менен value.
  • reject(error)— эгер ката кетсе, errorкатанын объектиси.

Ошентип, аткаруучу автоматтык түрдө иштей баштайт, ал ишти аткарып, анан чалышы керек resolveже reject.

promiseКонструктор кайтарган объекттин new Promiseички касиеттери бар:

  • state("стат") - адегенде ("күтүү"), андан кийин чакырганда ("ийгиликтүү") же чакырганда ("ката менен ийгиликтүү") "pending"өзгөрөт ."fulfilled"resolve"rejected"reject
  • result(«натыйжа») - адегенде undefined, андан кийин valueчалуу resolve(value)же errorчакыруу боюнча өзгөрөт reject(error).

Ошентип, аткаруучу акыры promiseэки абалдын бирине которот:

Кийинчерээк бул өзгөрүүлөрдү "күйөрмандар" кантип билишээрин карайбыз.

PromiseТөмөндө кечигүү менен натыйжа берген коду бар конструктор жана жөнөкөй аткаруучунун мисалы келтирилген (аркылуу setTimeout):

@A@let promise = new Promise(function(resolve, reject) {
  // эта функция выполнится автоматически, при вызове new Promise

  // через 1 секунду сигнализировать, что задача выполнена с результатом "done"
  setTimeout(() => resolve("done"), 1000);
});@A@

Жогорудагы кодду иштетүү менен эки нерсени байкай алабыз:

  1. Аткаруучу функция болгондо дароо иштейт new Promise.
  2. Аткаруучу эки аргументти алат: resolveжана rejectалар JavaScript'те курулган функциялар, ошондуктан аларды жазуунун кереги жок. Болгону, аткаруучу даяр болгондон кийин алардын бирине телефон чалганын текшеришибиз керек.

"Иштөөдөн" бир секунд өткөндөн кийин аткаруучу resolve("done")жыйынтык чыгаруу үчүн чакырат:

Бул ийгиликтүү аткарылган тапшырманын мисалы болду, натыйжада биз «ийгиликтүү аткарылды» деген убаданы алдык.

Ал эми азыр аткаруучу тапшырма ката менен аткарылганын билдире турган мисал:

@A@let promise = new Promise(function(resolve, reject) {
  // спустя одну секунду будет сообщено, что задача выполнена с ошибкой
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});@A@

Жыйынтыктап айтканда, аткаруучу тапшырманы аткарат (демейде убакытты талап кылган нерсе), андан кийин чалуу resolveже rejectтиешелүү абалын өзгөртүү Promise.

Убада - ийгиликтүү да, четке кагылган да, баштапкы убададан айырмаланып, "аткарылды" деп аталат "күтүүдө".

Бир нерсе болушу мүмкүн: натыйжа же ката

Аткаруучу бир нерсени чакырышы керек: resolveже reject. Убаданын абалын бир гана жолу өзгөртүүгө болот.

Бардык кийинки чалуулар resolveжана rejectкөңүл бурулбайт:

@A@let promise = new Promise(function(resolve, reject) {
  resolve("done");

  reject(new Error("…")); // игнорируется
  setTimeout(() => resolve("…")); // игнорируется
});@A@

Идея, аткаруучу тарабынан аткарылган тапшырма бир гана натыйжага ээ болушу мүмкүн: натыйжа же ката.

resolveОшондой эле / функциясы rejectбир гана аргументти (же эч кимди) күтөөрүн эске алыңыз. Бардык кошумча аргументтер этибарга алынбайт.

rejectОбъект менен чалууError

Бир нерсе туура эмес болуп калса, биз чалышыбыз керек reject. Муну аргументтин ар кандай түрү менен жасоого болот (менен болгон сыяктуу ), бирок объектти (же андан мураска калган) resolveколдонуу сунушталат . ErrorЭмнеге андай? Жакында түшүнөбүз.

Чалуу resolverejectдароо

Көбүнчө аткаруучу асинхрондук бир нерсе жасап, андан кийин resolve/ чалуу reject, башкача айтканда, бир нече убакыт өткөндөн кийин. Бирок бул талап кылынбайт, resolveже rejectдароо чакырылышы мүмкүн:

let promise = new Promise(function(resolve, reject) {
  // задача, не требующая времени
  resolve(123); // мгновенно выдаст результат: 123
});

Бул, мисалы, биз кандайдыр бир тапшырманы аткарып баштаганда, бирок ал буга чейин аткарылганын дароо көрүп, натыйжа кэштелгенде болушу мүмкүн.

Бул нормалдуу көрүнүш. Биз дароо ийгиликтүү аяктаган документти алабыз Promise.

касиеттери stateжана result- ички

жана касиеттери объекттин ички касиеттери stateжана биз аларга түз кире албайбыз. Натыйжаны иштетүү үчүн / / ыкмаларын колдонушуңуз керек , алар мындан ары талкууланат.resultPromise.then.catch.finally

Керектөөчүлөр: анда, кармагыла

Объект Promiseаткаруучу ("өндүрүүчү" код же "ырчы") жана натыйжаны же катаны ала турган керектөөчү функциялардын ("күйөрмандар") ортосундагы байланыш катары кызмат кылат. .thenКеректөөчү функцияларын жана ыкмаларын колдонуу менен каттоого (кол коюуга) болот .catch.

анда

Эң негизги жана негизги ыкмасы болуп саналат .then.

Синтаксис:

@A@promise.then(
  function(result) { /* обработает успешное выполнение */ },
  function(error) { /* обработает ошибку */ }
);@A@

Методдун биринчи аргументи .then- бул убада "ийгиликтүү" абалга кирип, натыйжаны алганда аткарылуучу функция.

Экинчи аргумент .then - бул убада "аткарылган жок" абалга кирип, ката алганда аткарылуучу функция.

Мисалы, бул жерде ийгиликтүү убадага болгон реакция:

@A@let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("done!"), 1000);
});

// resolve запустит первую функцию, переданную в .then
promise.then(
  result => alert(result), // выведет "done!" через одну секунду
  error => alert(error) // не будет запущена
);@A@

Биринчи функция аткарылды.

Ал эми убадада ката кетсе, экинчиси аткарылат:

@A@let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// reject запустит вторую функцию, переданную в .then
promise.then(
  result => alert(result), // не будет запущена
  error => alert(error) // выведет "Error: Whoops!" спустя одну секунду
);@A@

Эгерде биз тапшырманын ийгиликтүү аткарылышынын натыйжасына гана кызыкдар болсок, анда thenбир гана функцияга өтүүгө болот:

@A@let promise = new Promise(resolve => {
  setTimeout(() => resolve("done!"), 1000);
});

promise.then(alert); // выведет "done!" спустя одну секунду@A@

кармоо

Эгерде биз катаны гана чечүүнү кааласак, анда биз nullбиринчи аргумент катары колдоно алабыз: .then(null, errorHandlingFunction).catch(errorHandlingFunction)Же сиз ошол эле ыкманы колдоно аласыз :

@A@let promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("Ошибка!")), 1000);
});

// .catch(f) это то же самое, что promise.then(null, f)
promise.catch(alert); // выведет "Error: Ошибка!" спустя одну секунду@A@

Чакыруу .catch(f)- дын кыскартылган, "кыскартылган" версиясы .then(null, f).

Тазалоо: акыры

finallyКадимки блок сыяктуу эле try {...} catch {...}, убадалар да бар finally.

Чакыруу убада аткарылганда кандай гана учурда болбосун аткарылат деген мааниде : ийгиликтүү же ката менен .finally(f)окшош ..then(f, f)f

Идея finallyмурунку операциялар аяктагандан кийин тазалоо/аяктоо үчүн иштеткичти орнотуу.

Мисалы, жүктөө көрсөткүчтөрүн токтотуу, керексиз болгон байланыштарды жабуу ж.б.

Кеченин аягы деп ойлогула. Кече жакшы болдубу, жаман болдубу, канча достор бар эле, биз дагы деле аны тазалашыбыз керек (же жок дегенде биз керек).

Код мындай көрүнүшү мүмкүн:

@A@new Promise((resolve, reject) => {
  /* сделать что-то, что займёт время, и после вызвать resolve или может reject */
})
  // выполнится, когда промис завершится, независимо от того, успешно или нет
  .finally(() => остановить индикатор загрузки)
  // таким образом, индикатор загрузки всегда останавливается, прежде чем мы продолжим
  .then(result => показать результат, err => показать ошибку)@A@

Сураныч, көңүл буруңуз - бул сиз ойлогондой finally(f)псевдоним эмес .then(f,f)

Маанилүү айырмачылыктар бар:

  1. Чакырган иштеткичте finallyэч кандай аргумент жок. finallyУбада кандайча аткарылганын билбейбиз . Жана бул жакшы, анткени, адатта, биздин милдет "жалпы" акыркы жол-жоболорду аткаруу болуп саналат.

    Сураныч, жогорудагы мисалды карап чыгыңыз, анткени иштеп чыгуучунун finallyаргументтери жок жана убаданын натыйжасы кийинки иштеткичте каралат.

  2. Иштеп чыгуучу finallyнатыйжаны же катаны кийинки иштетүүчүлөргө "өтөт".

    Мисалы, бул жерде натыйжа finallyтөмөнкүгө өтөт then:

    @A@new Promise((resolve, reject) => {
      setTimeout(() => resolve("value"), 2000);
    })
      .finally(() => alert("Промис завершён")) // срабатывает первым
      .then(result => alert(result)); // <-- .then показывает "value"@A@

    Көрүнүп тургандай, биринчи убада менен кайтарылган маани finallyкийинкиге өтөт then.

    Бул абдан ыңгайлуу, анткени ал finallyубаданын натыйжасын иштетүүгө арналган эмес. Жогоруда айтылгандай, бул жалпы тазалоо жүргүзүү үчүн жер, эч кандай натыйжасы болгон.

    Бул жерде убаданын катасы finallyтөмөнкүгө өтөт catch:

    @A@new Promise((resolve, reject) => {
      throw new Error("error");
    })
      .finally(() => alert("Промис завершён")) // срабатывает первым
      .catch(err => alert(err));  // <-- .catch показывает ошибку@A@
  3. Иштөөчү finallyдагы эч нерсе кайтарбашы керек. Эгер ошондой болсо, кайтаруу мааниси унчукпай эске алынбайт.

    Бул эрежеден бир гана өзгөчөлүк - бул иштетүүчү finallyката кетиргенде. Андан кийин бул ката мурунку натыйжанын ордуна кийинки иштеткичке өтөт.

Жыйынтык чыгаруу:

  • Иштөөчү finallyмурунку иштеткичтин натыйжасын албайт (анын аргументтери жок). Анын ордуна, бул натыйжа кийинки тиешелүү иштеткичке өткөрүлүп берилет.
  • Эгер иштетүүчү finallyбир нерсени кайтарса, ага көңүл бурулбайт.
  • Качан finallyката кетиргенде, аткаруу эң жакын ката иштеткичке өтөт.

Бул функциялар пайдалуу жана бардыгын туура иштешин камсыздайт, эгерде биз аны finallyталап кылынгандай колдонсок: жалпы тазалоо процедуралары үчүн.

Аткарылган убадалар боюнча, иштеткичтер дароо ишке киргизилет

Эгерде убада күтүлбөгөн абалда болсо, анда иштегендер .then/catch/finallyаны күтүшөт.

Кээде биз ага иштеткичти кошкондо убада аткарылып калышы мүмкүн.

Мындай учурда, бул иштеткичтер дароо иштетилет:

@A@// при создании промиса он сразу переводится в состояние "успешно завершён"
let promise = new Promise(resolve => resolve("готово!"));

promise.then(alert); // готово! (выведется сразу)@A@

Мисал: loadScript

Эми убадалар бизге асинхрондук кодду жазууну жеңилдетет деген практикалык мисалдарды карап көрөлү.

loadScriptБизде мурунку бөлүмдөн сценарийди жүктөө функциясы бар .

Кайра чалуу опциясы кандай болгонун эстеп көрөлү:

@A@function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(null, script);
  script.onerror = () => callback(new Error(`Ошибка загрузки скрипта ${src}`));

  document.head.append(script);
}@A@

Эми аны колдонуп кайра жазалы Promise.

Жаңы функция loadScriptмындан ары аргументке муктаж эмес callbackPromiseАнын ордуна, ал жүктөө аяктагандан кийин "ийгиликтүү аяктаган" абалына өтө турган объектти түзүп, кайтарып берет . Тышкы код төмөнкүнү колдонуу менен иштеткичтерди ("абоненттерди") кошо алат .then:

@A@function loadScript(src) {
  return new Promise(function(resolve, reject) {
    let script = document.createElement('script');
    script.src = src;

    script.onload = () => resolve(script);
    script.onerror = () => reject(new Error(`Ошибка загрузки скрипта ${src}`));

    document.head.append(script);
  });
}@A@

Колдонмо:

@A@let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");

promise.then(
  script => alert(`${script.src} загружен!`),
  error => alert(`Ошибка: ${error.message}`)
);

promise.then(script => alert('Ещё один обработчик...'));@A@

Кайра чалуу ыкмасына караганда бир нече артыкчылыктар дароо байкалат:

Убадалар кайра чалуулар
Убадалар табигый тартипте иштерди кылууга мүмкүндүк берет. Алгач биз чуркайбыз loadScript(script), анан ( .then) натыйжа менен эмне кылууну жазабыз. callbackЧалуу учурунда бизде функция болушу керек loadScript(script, callback). Башкача айтканда, биз мурун натыйжа менен эмне кылуу керек экенин билишибиз керек loadScript.
.thenБиз сизге Promiseканча жолу кааласак, ошончо чалсак болот . Ар бир жолу биз жаңы "күйөрман" кошобуз, "жазылуулардын тизмесине" жаңы абоненттик функция. Бул тууралуу кийинки бөлүмдө: Убадаларды чынжырлоо . Бир гана кайра чалуу болушу мүмкүн.

Ошентип, убадалар бизге коддун тартибин жакшыртууга мүмкүндүк берет жана бизге ийкемдүүлүктү берет. Бирок бул баары эмес. Кийинки бөлүмдөрдө дагы көптөгөн пайдалуу нерселерди билебиз.

Tasks

 

Төмөнкү код эмнени чыгарат?

@A@let promise = new Promise(function(resolve, reject) {
  resolve(1);

  setTimeout(() => resolve(2), 1000);
});

promise.then(alert);@A@
чечим
 

Камтылган функция setTimeoutкайра чалуу функцияларын колдонот. Убадаларды колдонгон альтернатива түзүңүз.

Функция delay(ms)миллисекундда "бүттү" абалына өтө турган убаданы кайтарышы керек ms, ошондуктан биз ага кошо алабыз .then:

@A@function delay(ms) {
  // ваш код
}

delay(3000).then(() => alert('выполнилось через 3 секунды'));@A@
чечим
 

showCircleТапшырмада жазылган функцияны кайра жазыңыз Аргумент катары кайра чалуу функциясын кабыл алуунун ордуна убаданы кайтарып алуу үчүн, кайра чалуу аркылуу тегеректи жандандырыңыз.

Жаңы колдонуу:

@A@showCircle(150, 150, 100).then(div => {
  div.classList.add('message-ball');
  div.append("Hello, world!");
});@A@