Элестетиңиз, сиз атактуу ырчысыз, ал күйөрмандардын алдыдагы сингл тууралуу суроолору менен тынымсыз кыжаалат болуп турат.
Тыныгуу үчүн, сингл чыкканда аларга жөнөтүүгө убада бересиз. Сиз күйөрмандарга катталуу үчүн тизме бересиз. Алар ыр чыккандан кийин дароо алуу үчүн электрондук почтасын ошол жерге калтырса болот. Жана андан да көп: эгер бир нерсе туура эмес болуп кетсе, мисалы, студияда өрт чыгып, ыр чыкпай калса, алар дагы бул тууралуу билдирүү алышат.
Баары бактылуу! Сиз бактылуусуз, анткени күйөрмандар мындан ары сизди убара кылбайт жана күйөрмандар жаңы синглди өткөрүп жиберем деп тынчсызданбашы керек.
Бул программалоодо биз көп жолуккан жагдайлар үчүн чыныгы жашоо окшоштугу:
- Убакытты талап кылган нерсени жасаган "түзүү" код бар. Мисалы, тармак аркылуу маалыматтарды жүктөө. Биздин аналогияда бул “ырчы”.
- "Жаратуучу" коддун натыйжасын ал даяр болгондо алгысы келген "керектөөчү" код бар. Ал бир нече функцияга керек болушу мүмкүн. Булар күйөрмандар.
Promise
(англисчеpromise
, биз мындай объектти "убада" деп атайбыз) JavaScript'те "түзүү" жана "керектөө" коддорун бириктирген атайын объект. Биздин аналогия боюнча, бул "жазылуу үчүн тизме". "Түзүү" коду натыйжаны алуу үчүн канча убакыт талап кылынса, ошончо иштей алат, ал эми убада натыйжа даяр болгондо ага жазылган код үчүн натыйжаны жеткиликтүү кылат.
Promise
Аналогия толугу менен так эмес, анткени JavaScriptдеги объект жазылуулардын жөнөкөй тизмесине караганда алда канча татаал: анын кошумча мүмкүнчүлүктөрү жана чектөөлөрү бар. Бирок башталгычтар үчүн бул окшоштук жакшы.
Түзүү синтаксиси Promise
:
@A@let promise = new Promise(function(resolve, reject) {
// функция-исполнитель (executor)
// "певец"
});@A@
Конструкцияга берилген функция аткаруучуnew Promise
деп аталат . Түзүлгөндө , ал автоматтык түрдө башталат. Ал акыры натыйжа бере турган "түзүү" кодун камтышы керек. Биздин аналогия боюнча: аткаруучу – “ырчы”.Promise
Анын аргументтери resolve
жана reject
JavaScript өзү тарабынан берилген кайра чалуулар. Биздин код аткаруучунун ичинде гана.
Ал жыйынтыкты алганда, азыр же кийинчерээк, бул маанилүү эмес, бул кайра чалуулардын бирин чакырышы керек:
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@
Жогорудагы кодду иштетүү менен эки нерсени байкай алабыз:
- Аткаруучу функция болгондо дароо иштейт
new Promise
. - Аткаруучу эки аргументти алат:
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
Эмнеге андай? Жакында түшүнөбүз.
resolve
/ reject
дарооКөбүнчө аткаруучу асинхрондук бир нерсе жасап, андан кийин resolve
/ чалуу reject
, башкача айтканда, бир нече убакыт өткөндөн кийин. Бирок бул талап кылынбайт, resolve
же reject
дароо чакырылышы мүмкүн:
let promise = new Promise(function(resolve, reject) {
// задача, не требующая времени
resolve(123); // мгновенно выдаст результат: 123
});
Бул, мисалы, биз кандайдыр бир тапшырманы аткарып баштаганда, бирок ал буга чейин аткарылганын дароо көрүп, натыйжа кэштелгенде болушу мүмкүн.
Бул нормалдуу көрүнүш. Биз дароо ийгиликтүү аяктаган документти алабыз Promise
.
state
жана result
- ичкижана касиеттери объекттин ички касиеттери state
жана биз аларга түз кире албайбыз. Натыйжаны иштетүү үчүн / / ыкмаларын колдонушуңуз керек , алар мындан ары талкууланат.result
Promise
.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)
Маанилүү айырмачылыктар бар:
-
Чакырган иштеткичте
finally
эч кандай аргумент жок.finally
Убада кандайча аткарылганын билбейбиз . Жана бул жакшы, анткени, адатта, биздин милдет "жалпы" акыркы жол-жоболорду аткаруу болуп саналат.Сураныч, жогорудагы мисалды карап чыгыңыз, анткени иштеп чыгуучунун
finally
аргументтери жок жана убаданын натыйжасы кийинки иштеткичте каралат. -
Иштеп чыгуучу
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@
-
Иштөөчү
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
мындан ары аргументке муктаж эмес callback
. Promise
Анын ордуна, ал жүктөө аяктагандан кийин "ийгиликтүү аяктаган" абалына өтө турган объектти түзүп, кайтарып берет . Тышкы код төмөнкүнү колдонуу менен иштеткичтерди ("абоненттерди") кошо алат .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@