diff options
Diffstat (limited to 'files/uk/web/javascript/guide/using_promises')
| -rw-r--r-- | files/uk/web/javascript/guide/using_promises/index.html | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/files/uk/web/javascript/guide/using_promises/index.html b/files/uk/web/javascript/guide/using_promises/index.html new file mode 100644 index 0000000000..d3b986e51a --- /dev/null +++ b/files/uk/web/javascript/guide/using_promises/index.html @@ -0,0 +1,363 @@ +--- +title: Використання промісів +slug: Web/JavaScript/Guide/Using_promises +tags: + - JavaScript + - Promise + - Посібник + - Проміс + - асинхронний +translation_of: Web/JavaScript/Guide/Using_promises +--- +<div>{{jsSidebar("JavaScript Guide")}}</div> + +<div>{{PreviousNext("Web/JavaScript/Guide/Dokladno_pro_Objectnu_Model", "Web/JavaScript/Guide/Iterators_and_Generators")}}</div> + +<p>{{jsxref("Promise")}} (з англ. - "обіцянка", далі - "проміс") - це об'єкт, що відображає кінцеве завершення або невдачу асинхронної операції. Оскільки більшість людей є споживачами раніше створенних промісів, цей посібник спочатку пояснить споживання повернених промісів, а далі пояснить, як їх створювати.</p> + +<p>Проміс, по суті, є поверненим об'єктом, до якого ви прикріплюєте функції зворотного виклику, замість того, щоб передавати їх у функцію.</p> + +<p>Уявіть собі функцію <code>createAudioFileAsync()</code>, яка асинхронно генерує звуковий файл, маючи конфігураційний запис та дві функції зворотного виклику, одна викликається, якщо аудіофайл був успішно створений, а інша викликається, якщо виникає помилка.</p> + +<p>Ось код, який використовує <code>createAudioFileAsync()</code>:</p> + +<pre class="brush: js line-numbers language-js notranslate">function successCallback(result) { + console.log("Аудіофайл створений за адресою: " + result); +} + +function failureCallback(error) { + console.log("Під час створення аудіофайлу виникла помилка: " + error); +} + +createAudioFileAsync(audioSettings, successCallback, failureCallback); +</pre> + +<p>Замість цього, сучасні функції повертають проміс, до якого ви можете приєднати функції зворотного виклику:</p> + +<p>Якщо переписати функцію <code>createAudioFileAsync()</code>, щоб вона повертала проміс, її використання буде ось таким простим:</p> + +<pre class="brush: js notranslate">createAudioFileAsync(audioSettings).then(successCallback, failureCallback);</pre> + +<p>Це скорочений запис для:</p> + +<pre class="brush: js notranslate">const promise = createAudioFileAsync(audioSettings); +promise.then(successCallback, failureCallback);</pre> + +<p>Ми називаємо це <em>асинхронним викликом функції</em>. Ця конвенція має декілька переваг. Ми дослідимо кожну з них.</p> + +<h2 id="Гарантії">Гарантії</h2> + +<p>На відміну від старомодних колбеків, проміс постачається з певними гарантіями:</p> + +<ul> + <li>Функції зворотного виклику ніколи не будуть викликані до <a href="/uk/docs/Web/JavaScript/EventLoop">завершення поточного виконання</a> циклу подій JavaScript.</li> + <li>Функції зворотного виклику, додані за допомогою <code><a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/Promise/then">then()</a></code>, навіть <em>після</em> успіху або невдачі асинхронної операції, будуть викликані, як наведено вище.</li> + <li>Можна додати більше одного зворотного виклику, викликавши метод <code><a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/Promise/then">then()</a></code> декілька разів. Кожен зворотний виклик виконується один за одним, у тому порядку, в якому вони були додані.</li> +</ul> + +<p>Однією з величезних переваг промісів є <strong>ланцюгування</strong>.</p> + +<h2 id="Ланцюгування">Ланцюгування</h2> + +<p>Типовою потребою є виконання двох або більше асинхронних операцій одна за одною, коли кожна наступна операція починається, коли попередня успішно завершується, з результатом з попереднього кроку. Ми досягаємо цього, створюючи <strong>ланцюжок промісів</strong>.</p> + +<p>Ось вам магія: функція <code>then()</code> повертає <strong>новий проміс</strong>, що відрізняється від оригіналу:</p> + +<pre class="brush: js notranslate">const promise = doSomething(); +const promise2 = promise.then(successCallback, failureCallback); +</pre> + +<p>або</p> + +<pre class="brush: js notranslate">const promise2 = doSomething().then(successCallback, failureCallback); +</pre> + +<p>Цей другий проміс (<code>promise2</code>) представляє собою завершення не тільки <code>doSomething()</code>, але й <code>successCallback</code> або <code>failureCallback</code>, які ви передали, вони, в свою чергу, можуть бути іншими асинхронними функціями, що повертають проміс. В цьому випадку будь-які функції зворотного виклику, додані до <code>promise2</code>, стають в чергу за промісом, що повертається <code>successCallback</code> чи <code>failureCallback</code>.</p> + +<p>По суті, кожен проміс відображає завершення іншого асинхроннго кроку в ланцюжку.</p> + +<p>В старі часи виконання декількох асинхронних операцій підряд призвело б до класичної піраміди смерті з колбеків:</p> + +<pre class="brush: js notranslate">doSomething(function(result) { + doSomethingElse(result, function(newResult) { + doThirdThing(newResult, function(finalResult) { + console.log('Ось фінальний результат: ' + finalResult); + }, failureCallback); + }, failureCallback); +}, failureCallback); +</pre> + +<p>З сучасними функціями замість цього ми приєднуємо наші функції зворотного виклику до промісів, що повертаються, формуючи ланцюжок промісів.</p> + +<pre class="brush: js notranslate">doSomething().then(function(result) { + return doSomethingElse(result); +}) +.then(function(newResult) { + return doThirdThing(newResult); +}) +.then(function(finalResult) { + console.log('Ось фінальний результат: ' + finalResult); +}) +.catch(failureCallback); +</pre> + +<p>Аргументи до <code>then</code> є необов'язковими, а <code>catch(failureCallback)</code> - це скорочений запис для <code>then(null, failureCallback)</code>. Ви можете побачити це, виражене за допомогою <a href="/uk/docs/Web/JavaScript/Reference/Functions/Стрілкові_функції">стрілкових функцій</a>:</p> + +<pre class="brush: js notranslate">doSomething() +.then(result => doSomethingElse(result)) +.then(newResult => doThirdThing(newResult)) +.then(finalResult => { + console.log(`Ось фінальний результат: ${finalResult}`); +}) +.catch(failureCallback); +</pre> + +<p><strong>Важливо</strong>: Завжди повертайте результати, інакше функції зворотного виклику не перехоплять результат попереднього проміса (у стрілкових функціях <code>() => x</code> є скороченим записом для <code>() => { return x; }</code>).</p> + +<h3 id="Ланцюгування_після_catch">Ланцюгування після catch</h3> + +<p>Ланцюгувати <em>після </em>невдачі можливо, наприклад, <code>catch</code> є корисним у разі виконання нових операцій навіть після того, як операція у ланцюжку завершилась неуспішно. Дивіться наступний приклад:</p> + +<pre class="brush: js notranslate">new Promise((resolve, reject) => { + console.log('Початковий'); + + resolve(); +}) +.then(() => { + throw new Error('Щось пішло не так'); + + console.log('Зробити це'); +}) +.catch(() => { + console.log('Зробити те'); +}) +.then(() => { + console.log('Зробити це, що б не відбувалось раніше'); +}); + +</pre> + +<p>Це виведе наступний текст:</p> + +<pre class="notranslate">Початковий +Зробити те +Зробити це, що б не відбувалось раніше +</pre> + +<p><strong>Зауважте:</strong> Текст "Зробити це" не був виведений, тому що помилка "Щось пішло не так" спричинила відхилення.</p> + +<h2 id="Спливання_помилок">Спливання помилок</h2> + +<p>Ви, можливо, пригадуєте, що тричі бачили <code>failureCallback</code> раніше, у піраміді смерті, у порівнянні з лише одним викликом наприкінці ланцюжку промісів.</p> + +<pre class="brush: js notranslate">doSomething() +.then(result => doSomethingElse(value)) +.then(newResult => doThirdThing(newResult)) +.then(finalResult => console.log(`Ось фінальний результат: ${finalResult}`)) +.catch(failureCallback); +</pre> + +<p>Якщо виникає виняток, переглядач передивляється ланцюжок у пошуках обробників <code>catch</code> або <code>onRejected</code>. Це дуже схоже на модель того, як працює синхронний код:</p> + +<pre class="brush: js notranslate">try { + let result = syncDoSomething(); + let newResult = syncDoSomethingElse(result); + let finalResult = syncDoThirdThing(newResult); + console.log(`Ось фінальний результат: ${finalResult}`); +} catch(error) { + failureCallback(error); +} +</pre> + +<p>Ця симетрія з асинхронним кодом сягає кульмінації в синтаксичному цукрі <a href="/uk/docs/Web/JavaScript/Reference/Statements/async_function"><code>async</code>/<code>await</code></a> в ECMAScript 2017:</p> + +<pre class="brush: js notranslate">async function foo() { + try { + let result = await doSomething(); + let newResult = await doSomethingElse(result); + let finalResult = await doThirdThing(newResult); + console.log(`Ось фінальний результат: ${finalResult}`); + } catch(error) { + failureCallback(error); + } +} +</pre> + +<p>Він будується на промісах, наприклад, <code>doSomething()</code> - це та сама функція, що й раніше. Ви можете прочитати більше про синтаксис <a href="https://developers.google.com/web/fundamentals/primers/async-functions">тут</a>.</p> + +<p>Проміси виправляють фундаментальну хибу з пірамідою смерті, перехоплюючи всі помилки, навіть викинуті винятки та помилки программування. Це критично важливо для функціональної композиції асинхронних операцій.</p> + +<h2 id="Події_відхилення_промісів">Події відхилення промісів</h2> + +<p>Коли проміс відхиляється, одна з двох подій надсилається у глобальну область видимості (загалом, це або <a href="/uk/docs/Web/API/Window"><code>window</code></a>, або, при використанні у веб-виконавці, це <a href="/uk/docs/Web/API/Worker"><code>Worker</code></a> або інший інтерфейс на базі виконавців). Ці дві події наступні:</p> + +<dl> + <dt><a href="/uk/docs/Web/API/Window/rejectionhandled_event" title="Подія rejectionhandled надсилається у глобальну область видимості скрипта (зазвичай, window, але також і Worker), коли об'єкт JavaScript Promise відхиляється, але після того, як відхилення було оброблене."><code>rejectionhandled</code></a></dt> + <dd>Надсилається, коли проміс відхиляється, після того, як відхилення було оброблене функцією виконавця <code>reject</code>.</dd> + <dt><a href="/uk/docs/Web/API/Window/unhandledrejection_event" title="Подія unhandledrejection надсилається у глобальну область видимості скрипта, коли об'єкт JavaScript Promise, який не має обробника відхилення, відхиляється; зазвичай, це window, але також може бути Worker."><code>unhandledrejection</code></a></dt> + <dd>Надсилається, коли проміс відхиляється, але немає доступного обробника відхилення.</dd> +</dl> + +<p>У обох випадках подія (типу <a href="/uk/docs/Web/API/PromiseRejectionEvent" title="Інтерфейс PromiseRejectionEvent відображає події, які надсилаються у глобальний контекст скрипта, коли об'єкти JavaScript Promise відхиляються."><code>PromiseRejectionEvent</code></a>) має в якості полів властивість <a href="/uk/docs/Web/API/PromiseRejectionEvent/promise"><code>promise</code></a>, яка вказує відхилений проміс, та властивість <a href="/uk/docs/Web/API/PromiseRejectionEvent/reason"><code>reason</code></a>, яка передає надану причину відхилення проміса.</p> + +<p>Це робить можливою резервну обробку помилок для промісів, а також допомагає відлагоджувати проблеми в управлінні промісами. Ці обробники є глобальними за контекстом, тому усі помилки потраплятимуть в однакові обробники подій, незалежно від джерела.</p> + +<p>Один випадок особливої корисності: при написанні коду для {{Glossary("Node.js")}}, зазвичай, модулі, які ви включаєте у свій проект, можуть мати необроблені відхилені проміси. Вони виводяться у консоль середовищем виконання Node. Ви можете перехопити їх для аналізу та обробити у своєму коді — або просто уникнути захаращення ними виводу даних — додавши обробник події <a href="/uk/docs/Web/API/Window/unhandledrejection_event"><code>unhandledrejection</code></a>, ось так:</p> + +<pre class="brush: js notranslate">window.addEventListener("unhandledrejection", event => { + /* Ви можете почати тут, додавши код, щоб дослідити + вказаний проміс через event.promise та причину у + event.reason */ + + event.preventDefault(); +}, false);</pre> + +<p>Викликавши метод події <a href="/uk/docs/Web/API/Event/preventDefault" title="Метод інтерфейсу Event preventDefault() каже програмному агенту, що, якщо подія не була явно оброблена, її дія за замовчуванням не має виконуватись."><code>preventDefault()</code></a>, ви кажете середовищу виконання JavaScript не виконувати дію за замовчуванням, коли відхилений проміс лишається необробленим. Ця дія зазвичай містить виведення помилки у консоль, а це якраз випадок для Node.</p> + +<p>В ідеалі, звісно, ви маєте досліджувати відхилені проміси, щоб бути певними, що жоден з них не є насправді помилкою коду, перед тим, як відкидати ці події.</p> + +<h2 id="Створення_промісу_на_основі_старого_API_зі_зворотним_викликом">Створення промісу на основі старого API зі зворотним викликом</h2> + +<p>{{jsxref("Promise","Проміс")}} може бути створенний з нуля за допогою свого конструктора. Це необхідно лише для обгортки старих API.</p> + +<p>В ідеальному світі всі асинхронні функції повертали б проміси. На жаль, деякі API досі очікують старомодну передачу функцій зворотного виклику для успіху та/або невдачі. Найбільш очевидним прикладом є функція {{domxref("WindowTimers.setTimeout", "setTimeout()")}} :</p> + +<pre class="brush: js notranslate">setTimeout(() => saySomething("Пройшло 10 секунд"), 10000); +</pre> + +<p>Змішування старомодних зворотних викликів та промісів є проблематичним. Якщо <code>saySomething</code> завершиться невдачею або міститиме помилку программування, ніщо її не перехопить.</p> + +<p>На щастя, ми можемо обгорнути <code>saySomething</code> у проміс. Найкращою практикою вважається обгортати проблематичні функції на якомога нижчому рівні і вже ніколи не звертатись до них прямо.</p> + +<pre class="brush: js notranslate">const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); + +wait(10000).then(() => saySomething("10 секунд")).catch(failureCallback); +</pre> + +<p>По суті, конструктор промісу приймає функцію виконання, яка дозволяє вирішити або відхилити проміс вручну. Оскільки <code>setTimeout</code>, насправді, ніколи не завершується невдало, ми пропускаємо функцію відхилення в цьому випадку.</p> + +<h2 id="Композиція">Композиція</h2> + +<p>Методи {{jsxref("Promise.resolve()")}} та {{jsxref("Promise.відхилено", "Promise.reject()")}} є скороченими записами для створення вручну вже вирішених або відхилених промісів відповідно. Інколи це може бути корисно.</p> + +<p>Методи {{jsxref("Promise.all()")}} та {{jsxref("Promise.race()")}} є двома інструментами композиції для паралельного виконання асинхронних операції.</p> + +<p>Ми можемо почати операції паралельно та чекати, доки вони усі не завершаться ось так:</p> + +<pre class="brush: js notranslate">Promise.all([func1(), func2(), func3()]) +.then(([result1, result2, result3]) => { /* використати result1, result2 та result3 */ });</pre> + +<p>Послідовна композиція можлива з використанням певного розумного JavaScript:</p> + +<pre class="brush: js notranslate">[func1, func2, func3].reduce((p, f) => p.then(f), Promise.resolve()) +.then(result3 => { /* використати result3 */ });</pre> + +<p>По суті, ми зменшуємо масив асинхронних функцій до ланцюжка промісів, еквівалентного: <code>Promise.resolve().then(func1).then(func2).then(func3);</code></p> + +<p>Це можна перетворити на композиційну функцію багаторазового використання, що є типовим у функціональному програмуванні:</p> + +<pre class="brush: js notranslate">const applyAsync = (acc,val) => acc.then(val); +const composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));</pre> + +<p>Функція <code>composeAsync()</code> прийме будь-яку кількість функцій в якості аргументів і поверне нову функцію, яка приймає початкове значення, що має пройти крізь конвеєр композиції:</p> + +<pre class="brush: js notranslate">const transformData = composeAsync(func1, func2, func3); +const result3 = transformData(data); +</pre> + +<p>В ECMAScript 2017 послідовну композицію можна виконати простіше, за допомогою async/await:</p> + +<pre class="brush: js notranslate">let result; +for (const f of [func1, func2, func3]) { + result = await f(result); +} +/* використати останній результат (тобто, result3) */</pre> + +<h2 id="Хронометраж">Хронометраж</h2> + +<p>Щоб уникнути сюрпризів, функції, передані до <code>then()</code>, ніколи не викликатимуться синхронно, навіть для вже вирішеного проміса:</p> + +<pre class="brush: js notranslate">Promise.resolve().then(() => console.log(2)); +console.log(1); // 1, 2 +</pre> + +<p>Замість негайного виконання, передані функції ставляться у чергу мікрозадач. Це означає, що вони виконуються пізніше, коли черга стає порожньою в кінці поточного виконання циклу подій JavaScript, тобто, досить скоро:</p> + +<pre class="brush: js notranslate">const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); + +wait().then(() => console.log(4)); +Promise.resolve().then(() => console.log(2)).then(() => console.log(3)); +console.log(1); // 1, 2, 3, 4 +</pre> + +<h2 id="Вкладеність">Вкладеність</h2> + +<p>Прості ланцюжки промісів найкраще тримати рівними, без вкладень, оскільки вкладення можуть бути результатом недбалої композиції. Дивіться <a href="#Типові_помилки">типові помилки</a>.</p> + +<p>Вкладеність є контролюючою структурою для обмеження області видимості блоків <code>catch</code>. Зокрема, вкладений <code>catch</code> перехоплює лише помилки своєї області видимості та нижче, але не ті помилки, що знаходяться вище у ланцюжку поза вкладеною областю видимості. При правильному використанні це надає більшу точність у виявленні помилок:</p> + +<pre class="brush: js notranslate">doSomethingCritical() +.then(result => doSomethingOptional(result) + .then(optionalResult => doSomethingExtraNice(optionalResult)) + .catch(e => {})) // Ігнорувати, якщо не працює щось другорядне; продовжити. +.then(() => moreCriticalStuff()) +.catch(e => console.error("Критична помилка: " + e.message));</pre> + +<p>Зауважте, що необов'язкові кроки тут вкладені, не для відступів, але для передбачливого розташування зовнішніх дужок <code>(</code> та <code>)</code> навколо.</p> + +<p>Внутрішній нейтралізуючий блок <code>catch</code> перехоплює помилки тільки від <code>doSomethingOptional()</code> та <code>doSomethingExtraNice()</code>, після чого виконання коду продовжується у <code>moreCriticalStuff()</code>. Що важливо, якщо функція <code>doSomethingCritical()</code> завершується невдало, її помилка перехоплюється тільки кінцевим (зовнішнім) блоком <code>catch</code>.</p> + +<h2 id="Типові_помилки">Типові помилки</h2> + +<p>Ось деякі типові помилки, яких варто остерігатися при складанні ланцюжків промісів. Декілька з цих помилок проявляються у наступному прикладі:</p> + +<pre class="brush: js example-bad notranslate">// Поганий приклад! Помічено 3 помилки! + +doSomething().then(function(result) { + doSomethingElse(result) // Забули повернути проміс з внутрішнього ланцюжка + непотрібне вкладення + .then(newResult => doThirdThing(newResult)); +}).then(() => doFourthThing()); +// Забули завершити ланцюжок блоком catch! +</pre></pre> + +<p>Перша помилка - не завершувати ланцюжки як слід. Це відбувається, коли ми створюємо новий проміс, але забуваємо його повернути. Як наслідок, ланцюг переривається, чи, скоріше, ми отримуємо два конкуруючі ланцюжки. Це означає, що <code>doFourthThing()</code> не чекатиме на завершення <code>doSomethingElse()</code> чи <code>doThirdThing()</code> і запуститься паралельно з ними, скоріше за все, ненавмисно. В окремих ланцюжках також окремо обробляються помилки, що призводить до неперехоплених помилок.</p> + +<p>Друга помилка - непотрібна вкладеність, що уможливлює першу помилку. Вкладеність також обмежує область видимості внутрішніх обробників помилок, а це — якщо зроблене ненавмисно — може призвести до неперехоплених помилок. Варіантом цього є <a href="https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it">антишаблон конструювання промісів</a>, який поєднує вкладення з надлишковим використанням конструктора промісів для загортання коду, який вже використовує проміси.</p> + +<p>Третя помилка - забувати завершувати ланцюжки блоком <code>catch</code>. Незавершені ланцюжки промісів призводять до неперехоплених відхилень промісів у більшості переглядачів.</p> + +<p>Гарний практичний підхід - завжди або повертати, або завершувати ланцюжки промісів, і, як тільки ви отримуєте новий проміс, повертати його негайно, щоб вирівняти ланцюжок:</p> + +<pre class="brush: js example-good notranslate">doSomething() +.then(function(result) { + return doSomethingElse(result); +}) +.then(newResult => doThirdThing(newResult)) +.then(() => doFourthThing()) +.catch(error => console.error(error));</pre> + +<p>Зауважте, що <code>() => x</code> є скороченням для <code>() => { return x; }</code>.</p> + +<p>Тепер ми маємо єдиний, детермінований ланцюжок з правильною обробкою помилок.</p> + +<p>Використання <a href="/uk/docs/Web/JavaScript/Reference/Statements/async_function"><code>async</code>/<code>await</code></a> вирішує більшість, якщо не всі ці проблеми — натомість, найпоширенішою помилкою при використанні цього синтаксису є забуте ключове слово <a href="/uk/docs/Web/JavaScript/Reference/Statements/async_function"><code>await</code></a>.</p> + +<h2 id="Коли_зустрічаються_задачі_та_проміси">Коли зустрічаються задачі та проміси</h2> + +<p>Якщо ви стикаєтеся з ситуаціями, коли у вас є проміси та задачі (такі, як події або зворотні виклики), які запускаються у непередбачуваному порядку, можливо, вам буде корисно скористатись мікрозадачами, щоб перевірити статус або збалансувати проміси, коли створення промісів залежить від певних умов.</p> + +<p>Якщо ви вважаєте, що мікрозадачі могли б вирішити цю проблему, дивіться <a href="/uk/docs/Web/API/HTML_DOM_API/Microtask_guide">посібник з мікрозадач</a>, щоб дізнатись, як використовувати метод <code><a href="/uk/docs/Web/API/WindowOrWorkerGlobalScope/queueMicrotask" title="The queueMicrotask() method, which is exposed on the Window or Worker interface, queues a microtask to be executed at a safe time prior to control returning to the browser's event loop.">queueMicrotask()</a></code>, щоб поставити функцію у чергу як мікрозадачу.</p> + +<h2 id="Дивіться_також">Дивіться також</h2> + +<ul> + <li>{{jsxref("Promise.then()")}}</li> + <li><a href="/uk/docs/Web/JavaScript/Reference/Statements/async_function"><code>async</code>/<code>await</code></a> </li> + <li><a href="http://promisesaplus.com/">Promises/A+ specification</a></li> + <li><a href="https://medium.com/@ramsunvtech/promises-of-promise-part-1-53f769245a53">Venkatraman.R - JS Promise (Part 1, Basics)</a></li> + <li><a href="https://medium.com/@ramsunvtech/js-promise-part-2-q-js-when-js-and-rsvp-js-af596232525c#.dzlqh6ski">Venkatraman.R - JS Promise (Part 2 - Using Q.js, When.js and RSVP.js)</a></li> + <li><a href="https://tech.io/playgrounds/11107/tools-for-promises-unittesting/introduction">Venkatraman.R - Tools for Promises Unit Testing</a></li> + <li><a href="http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html">Nolan Lawson: We have a problem with promises — Common mistakes with promises</a></li> +</ul> + +<p>{{PreviousNext("Web/JavaScript/Guide/Dokladno_pro_Objectnu_Model", "Web/JavaScript/Guide/Iterators_and_Generators")}}</p> |
