diff --git a/1-js/11-async/03-promise-chaining/article.md b/1-js/11-async/03-promise-chaining/article.md index 4952d1b20..945a9cdfd 100644 --- a/1-js/11-async/03-promise-chaining/article.md +++ b/1-js/11-async/03-promise-chaining/article.md @@ -1,7 +1,7 @@ # Ланцюжок промісів -Повернемося до проблеми, згаданої в розділі : у нас є послідовність асинхронних задач, які потрібно виконувати одну за одною — наприклад, завантаження скриптів. Як ми можемо це добре закодувати? +Повернемося до проблеми, згаданої в розділі : у нас є послідовність асинхронних задач, які потрібно виконувати одну за одною — наприклад, завантаження скриптів. Як ми можемо написати код зручно і зрозуміло? Проміси надають кілька способів вирішення подібних задач. @@ -46,7 +46,7 @@ new Promise(function(resolve, reject) { Усе це працює тому, що кожний виклик `.then` повертає новий проміс, тому ми можемо викликати наступний `.then` на ньому. -Коли обробник повертає значення, воно стає результатом того промісу, тому наступний `.then` викликається з цим значенням. +Коли обробник повертає значення, воно стає результатом його промісу, тому наступний `.then` викликається з цим значенням. **Класична помилка новачка: технічно ми також можемо додати багато `.then` до одного промісу. Та це не ланцюжок.** @@ -72,7 +72,7 @@ promise.then(function(result) { }); ``` -Те, що ми зробили тут -- це додали лише кілька обробників до одного промісу. Вони не передають результат один одному; натомість вони обробляють його самостійно. +Те, що ми зробили тут -- це просто додали кілька обробників до одного промісу. Вони не передають результат один одному ланцюжком. Натомість кожен по своєму обробляє результат одного і того ж проміса. Ось малюнок (порівняйте його з ланцюжком вище): @@ -86,7 +86,7 @@ promise.then(function(result) { Обробник, використанний в `.then(handler)` може створити й повернути проміс. -У цьому випадку інші обробники чекають, поки він виконається, а потім отримають його результат. +У цьому випадку інші обробники чекають, поки він виконається, а потім отримують його результат. Наприклад: @@ -120,7 +120,7 @@ new Promise(function(resolve, reject) { }); ``` -Тут перший `.then` показує `1` і повертає `new Promise(…)` у рядку `(*)`. Через одну секунду він вирішується, а результат (аргумент `resolve`, тут це `result * 2`) передається обробнику другого `.then`. Цей обробник знаходиться в рядку `(**)`, він показує `2` і робить те ж саме. +Тут перший `.then` показує `1` і повертає `new Promise(…)` у рядку `(*)`. Через одну секунду він завершується, а результат (аргумент `resolve`, тут це `result * 2`) передається обробнику другого `.then`. Цей обробник знаходиться в рядку `(**)`, він показує `2` і робить те ж саме. Отже, результат такий же, як і в попередньому прикладі: 1 -> 2 -> 4, але тепер із затримкою в 1 секунду між викликами `alert`. @@ -128,7 +128,7 @@ new Promise(function(resolve, reject) { ## Приклад: loadScript -Давайте використовувати цю можливість з промісифікацією `loadScript`, визначеною у [попередньому розділі](info:promise-basics#loadscript), щоб завантажувати скрипти один за одним, у послідовності: +Давайте використовувати цю можливість з промісифікацією `loadScript`, описаною у [попередньому розділі](info:promise-basics#loadscript), щоб завантажувати скрипти один за одним, у послідовності: ```js run loadScript("/article/promise-chaining/one.js") @@ -162,7 +162,7 @@ loadScript("/article/promise-chaining/one.js") ``` -Тут кожен виклик `loadScript` повертає проміс, а наступний `.then` запускається, коли він виконується. Потім він ініціює завантаження наступного сценарію. Таким чином, скрипти завантажуються один за одним. +Тут кожен виклик `loadScript` повертає проміс, а наступний `.then` запускається, коли він виконується. Потім він ініціює завантаження наступного. Таким чином, скрипти завантажуються один за одним. Ми можемо додати більше асинхронних дій до ланцюжка. Зверніть увагу, що код все ще "плоский" — він росте вниз, а не вправо. Немає жодних ознак "піраміди приреченості". @@ -183,9 +183,9 @@ loadScript("/article/promise-chaining/one.js").then(script1 => { Цей код робить те ж саме: завантажує 3 скрипти послідовно. Але він "росте вправо". Тож у нас та ж проблема, що й з колбеками. -Люди, які починають використовувати проміси, іноді не знають про ланцюжок, тому пишуть це так. Як правило, перевага віддається ланцюжкам. +Люди, які починають використовувати проміси, іноді не знають про ланцюжок, тому пишуть це так. Але зазвичай краще писати "ланцюжками". -Іноді нормально писати `.then` відразу, оскільки вкладена функція має доступ до зовнішньої області видимості. У наведеному вище прикладі найбільш вкладений колбек має доступ до всіх змінних `script1`, `script2`, `script3`. Але це швидше виняток, ніж правило. +Іноді справді буває потреба писати `.then` всередині. Для того, щоб вкладена функція мала доступ до зовнішньої області видимості. У наведеному вище прикладі найбільш вкладений колбек має доступ до всіх змінних `script1`, `script2`, `script3`. Але така потреба це швидше виняток, ніж правило. ````smart header="Thenables" @@ -216,7 +216,7 @@ new Promise(resolve => resolve(1)) .then(alert); // показує 2 через 1000 мс ``` -JavaScript перевіряє об’єкт, повернутий обробником `.then` у рядку `(*)`: якщо він має викликаний метод з ім’ям `then`, тоді він викликає цей метод, та надає власні функції `resolve`, `reject` як аргументи (подібно виконавцю) і чекає, поки один з них не буде викликаний. У наведеному вище прикладі `resolve(2)` викликається через 1 секунду `(**)`. Потім результат передається далі по ланцюжку. +JavaScript перевіряє об’єкт, повернутий обробником `.then` у рядку `(*)`: якщо він має придатний до виклику метод з ім’ям `then`, тоді він викликає цей метод, та передає йому власні функції `resolve`, `reject` як аргументи (подібно виконавцю) і чекає, поки один з них не буде викликаний. У наведеному вище прикладі `resolve(2)` викликається через 1 секунду `(**)`. Потім результат передається далі по ланцюжку. Ця функція дозволяє нам інтегрувати власні об’єкти з ланцюжками промісів без успадкування від `Promise`. ```` @@ -232,27 +232,27 @@ JavaScript перевіряє об’єкт, повернутий обробни let promise = fetch(url); ``` -Зазначений код робить мережевий запит до `url` і повертає проміс. Проміс розв’язується за допомогою об’єкта `response`, коли віддалений сервер відповідає заголовками, але *до завантаження повної відповіді*. +Зазначений код робить мережевий запит до `url` і повертає проміс. Цей проміс переходить в стан "fullfilled" і його `value` стає об'єкт `response` (іншими словами, проміс завершується об'єктом `response`) як тільки віддалений сервер присилає заголовки, але ще *до завантаження повної відповіді*. -Щоб прочитати повну відповідь, ми повинні викликати метод `response.text()`: він повертає проміс, який виконується, коли повний текст завантажується з віддаленого сервера, і містить цей текст як результат. +Щоб прочитати повну відповідь, ми повинні викликати метод `response.text()`, він в свою чергу повертає новий проміс. Цей новий проміс завершується лише тоді, коли з віддаленого сервера завантажується увесь текст, не лише заголовки. Після завершення він буде містити весь текст відповіді в якості результату. Наведений нижче код робить запит до `user.json` і завантажує його текст із сервера: ```js run fetch('/article/promise-chaining/user.json') - // .then нижче запускається, коли віддалений сервер відповідає + // .then нижче запускається, коли віддалений сервер надіслав заголовки .then(function(response) { - // response.text() повертає новий проміс, який вирішується з повним текстом відповіді, - // коли він завантажується + // response.text() повертає новий проміс, який завершується повним текстом відповіді, + // після того, як текст повністю завантажиться return response.text(); }) .then(function(text) { - // ...а ось вміст віддаленого файлу + // ...а ось і повний вміст віддаленого файлу alert(text); // {"name": "iliakan", "isAdmin": true} }); ``` -Об’єкт `response`, повернутий із `fetch`, також включає метод `response.json()`, який зчитує віддалені дані у форматі JSON. У нашому випадку це ще зручніше, тож перейдемо до нього. +Об’єкт `response`, повернутий із `fetch`, також має метод `response.json()`, який обробляє отриманий текст і зберігає його у форматі JSON. У нашому випадку він ще зручніший за метод `response.text()`, тому далі будемо використовувати саме його. Ми також будемо використовувати стрілкові функції для стислості: @@ -287,7 +287,7 @@ fetch('/article/promise-chaining/user.json') }); ``` -Код працює; дивіться коментарі щодо деталей. Однак у цьому є потенційна проблема, типова помилка для тих, хто починає використовувати проміси. +Код працює; дивіться коментарі для деталей. Однак у цьому є потенційна проблема, типова помилка для тих, хто починає використовувати проміси. Подивіться на рядок `(*)`: як ми можемо щось зробити *після* того, як аватар закінчить відображатися і видалиться? Наприклад, ми хотіли б показати форму для редагування цього користувача чи щось інше. Поки що такої можливості немає. @@ -321,9 +321,9 @@ fetch('/article/promise-chaining/user.json') Тобто обробник `.then` у рядку `(*)` тепер повертає `new Promise`, який перейде у стан "виконаний" лише після виклику `resolve(githubUser)` у `setTimeout` `(**)`. Наступний `.then` в ланцюжку буде чекати цього. -Як хороша практика, асинхронна дія завжди повинна повертати проміс. Це дає можливість планувати дії після нього; навіть якщо ми не плануємо розширювати ланцюжок зараз, можливо, це нам знадобиться пізніше. +Гарним тоном буде писати код так, щоб асинхронна дія завжди повертала проміс. Це дає можливість іншим програмістам після вас планувати наступні дії, які зможуть почекати завершення попередньої; навіть якщо ми не плануємо розширювати ланцюжок зараз, можливо, це нам знадобиться пізніше. -Нарешті, ми можемо розділити код на функції, що можуть бути перевикористані: +Ну а зараз ми можемо розділити код на функції, що можуть бути перевикористані: ```js run function loadJson(url) { @@ -359,7 +359,7 @@ loadJson('/article/promise-chaining/user.json') ## Підсумки -Якщо обробник `.then` (або `catch/finally`, не має різниці) повертає проміс, решта ланцюжка чекає, доки він виконається. Коли це відбувається, його результат (або помилка) передається далі. +Якщо обробник `.then` (або `catch/finally`, однаково) повертає проміс, решта ланцюжка чекає, доки він виконається. Коли це відбувається, його результат (або помилка) передається далі. Ось повна картина: