Убада чынжырлары каталарды кармоо үчүн сонун. Эгер убада ишке ашпаса, башкаруу эң жакынкы ката иштетүүчүгө өтөт. Иш жүзүндө бул абдан ыңгайлуу.
Мисалы, төмөнкү мисалда fetch
URL жараксыз (веб-сайт жок) жана .catch
ал катаны кармайт:
@A@fetch('https://no-such-server.blabla') // ошибка
.then(response => response.json())
.catch(err => alert(err)) // TypeError: failed to fetch (текст может отличаться)@A@
Көрүнүп тургандай, .catch
катадан кийин дароо болушу керек эмес, ал бир же бир нечеден кийин дагы болушу мүмкүн.then
Же серверде баары жакшы болушу мүмкүн, бирок биз жоопто туура эмес JSON алабыз. Бардык каталарды кармоонун эң оңой жолу - .catch
чынжырдын аягына кошуу:
@A@fetch('/article/promise-chaining/user.json')
.then(response => response.json())
.then(user => fetch(`https://api.github.com/users/${user.name}`))
.then(response => response.json())
.then(githubUser => new Promise((resolve, reject) => {
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
setTimeout(() => {
img.remove();
resolve(githubUser);
}, 3000);
}))
.catch(error => alert(error.message));@A@
Эгер баары жайында болсо, анда бул .catch
такыр аткарылбайт. Бирок убадалардын бири четке кагылса (тармак көйгөйлөрү же туура эмес json саптары же башкасы), анда ката кармалып калат.
толук эмес... try catch
Убада функциясынын жана иштетүүчүлөрдүн айланасында "көрүнбөгөн" бар try..catch
. Эгерде өзгөчөлүк пайда болсо, ал кармалып, бул ката менен убада четке кагылган деп эсептелет.
Мисалы, бул код:
@A@new Promise((resolve, reject) => {
throw new Error("Ошибка!");
}).catch(alert); // Error: Ошибка!
... ушул сыяктуу иштейт:
new Promise((resolve, reject) => {
reject(new Error("Ошибка!"));
}).catch(alert); // Error: Ошибка!@A@
try..catch
Убаданын тегерегиндеги "көрүнбөгөн " катаны автоматтык түрдө кармап, аны четке кагылган убадага айлантат.
Бул убада функциясында гана эмес, иштетүүчүлөрдө да иштейт. throw
Эгерде биз ( ) иштеткичтен ( ) ката кетирсек .then
, анда убада четке кагылды деп эсептелинет жана башкаруу эң жакынкы ката иштетүүчүгө өтөт.
Мисал:
@A@new Promise((resolve, reject) => {
resolve("ок");
}).then((result) => {
throw new Error("Ошибка!"); // генерируем ошибку
}).catch(alert); // Error: Ошибка!@A@
Бул бардык каталар үчүн гана эмес, throw
. Мисалы, программалык камсыздоо катасы:
@A@new Promise((resolve, reject) => {
resolve("ок");
}).then((result) => {
blabla(); // нет такой функции
}).catch(alert); // ReferenceError: blabla is not defined@A@
Акыркысы чакырылган .catch
убадаларды да reject
, иштеткичтердеги туш келди каталарды да кармайт.
Каталарды ыргытyy
Биз буга чейин байкагандай, .catch
ал сыяктуу иш-аракет кылат try..catch
. .then
Биз каалагандай көп иштеткичтерге ээ боло алабыз жана андан соң .catch
бардык иштеткичтерден каталарды кармоо үчүн бирөөнү колдонсок болот.
Кадимкидей try..catch
, катаны талдап, аны чече албасак, аны кайра ыргыта алабыз. Ошол эле убадалар үчүн да мүмкүн.
throw
Эгерде биз блоктун ичине ката кетирсек ( ) .catch
, башкаруу кийинки эң жакын ката иштетүүчүгө өтөт. Жана эгер биз катаны иштетип, иштеткичтен кадимкидей чыксак, анда кийинки ийгиликтүү иштеткич иштей берет .then
.
Төмөндөгү мисал .catch
катаны ийгиликтүү чечет:
@A@// the execution: catch -> then
new Promise((resolve, reject) => {
throw new Error("Ошибка!");
}).catch(function(error) {
alert("Ошибка обработана, продолжить работу");
}).then(() => alert("Управление перейдёт в следующий then"));@A@
Бул жерде блок .catch
кадимкидей бүтөт. Ошондуктан, кийинки ийгиликтүү иштеткич деп аталат .then
.
Төмөндөгү мисалда биз блок менен башкача кырдаалды көрөбүз .catch
. Иштегич (*)
катаны кармайт жана аны иштете албайт (мисалы, ал кантип иштетүүнү гана билет URIError
), ошондуктан ката андан ары жөнөтүлөт:
@A@// the execution: catch -> catch -> then
new Promise((resolve, reject) => {
throw new Error("Ошибка!");
}).catch(function(error) { // (*)
if (error instanceof URIError) {
// обрабатываем ошибку
} else {
alert("Не могу обработать ошибку");
throw error; // пробрасывает эту или другую ошибку в следующий catch
}
}).then(function() {
/* не выполнится */
}).catch(error => { // (**)
alert(`Неизвестная ошибка: ${error}`);
// ничего не возвращаем => выполнение продолжается в нормальном режиме
});@A@
Башкаруу биринчи блоктон .catch
(*)
экинчисине (**)
, чынжыр боюнча өтөт.
Обработкаланбаган каталар
Ката чечилбесе эмне болот? Мисалы, биз .catch
чынжырдын аягына мындайча кошууну унутуп калдык:
@A@new Promise(function() {
noSuchFunction(); // Ошибка (нет такой функции)
})
.then(() => {
// обработчики .then, один или более
}); // без .catch в самом конце!@A@
Ката болгондо, аткаруу эң жакын ката иштетүүчүгө өтүшү керек. Бирок жогорудагы мисалда эч кандай иштетүүчү жок. Ошондуктан, ката "жабылат" дегендей, аны иштете турган эч ким жок.
Иш жүзүндө, коддогу кадимки иштетилбеген каталардай эле, бул бир нерсе туура эмес болгонун билдирет.
Кадимки ката кармалбаса эмне болот try..catch
? Скрипт консолдогу билдирүү менен өлөт. Ушундай эле нерсе чечилбеген убада катасында болот.
JavaScript кыймылдаткычы мындай жагдайларды көзөмөлдөйт жана бул учурда глобалдык катаны жаратат. Жогорудагы мисалды иштетсеңиз, аны консолдон көрө аласыз.
Браузерде окуяны колдонуп, мындай каталарды таба алабыз unhandledrejection
:
@A@window.addEventListener('unhandledrejection', function(event) {
// объект события имеет два специальных свойства:
alert(event.promise); // [object Promise] - промис, который сгенерировал ошибку
alert(event.reason); // Error: Ошибка! - объект ошибки, которая не была обработана
});
new Promise(function() {
throw new Error("Ошибка!");
}); // нет обработчика ошибок@A@
Бул окуя HTML стандартынын бир бөлүгү болуп саналат .
Эгерде ката пайда болуп, иштеткич жок болсо, анда окуя түзүлөт unhandledrejection
жана тиешелүү объект event
ката жөнүндө маалыматты камтыйт.
Адатта, мындай каталар өлүмгө алып келет, андыктан колдонуучуга көйгөй тууралуу маалымат берүү жана ката тууралуу маалыматты серверге жөнөтүү эң жакшы.
Node.js сыяктуу браузерден тышкары чөйрөлөрдө иштетилбеген каталарды көзөмөлдөөнүн башка жолдору бар.
Бардыгы
.catch
убадалардагы ар кандай каталарды кармайт: чалуулар болобуreject()
же иштеткичте ташталган ката болобуthrow
..then
экинчи аргумент (ката иштеткич болуп саналат) берилсе, ошондой эле каталарды кармайт..catch
Биз аны каталарды чечүүнү каалаган жерге жайгаштырышыбыз керек жана аны кантип жасоону билишибиз керек. Иштетүүчү катаны талдай алат (өзгөчө ката класстары пайдалуу болушу мүмкүн) жана ал жөнүндө эч нерсе билбесе, аны кайра ыргыта алат (балким, бул программалоо катасыдыр)..catch
Катадан калыбына келтирүүнүн кадимки жолу жок болсо, аны такыр колдоно албайсыз .- Кандай болгон күндө да, иштетилбеген каталарга көз салуу жана колдонуучуга (мүмкүн биздин серверге) маалымат берүү үчүн окуяны иштеткичти (браузерлер үчүн жана башка чөйрөлөр үчүн) колдонушубуз керек
unhandledrejection
, ошондо биздин тиркеме эч качан "жөн эле өлбөйт".
Tasks
Сиз кандай ойлойсуз? Ал аткарылабы .catch
? Жообуңду түшүндүр.
@A@new Promise(function(resolve, reject) {
setTimeout(() => {
throw new Error("Whoops!");
}, 1000);
}).catch(alert);@A@