Created
July 18, 2018 08:13
-
-
Save YozhEzhi/4196331d5e7aacbf721f06ec6bc18fd0 to your computer and use it in GitHub Desktop.
Promises.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Задачки на проверку работы промисов. | |
| */ | |
| doSomething().then(function () { | |
| return doSomethingElse(); | |
| }); | |
| doSomething().then(function () { | |
| doSomethingElse(); | |
| }); | |
| doSomething().then(doSomethingElse()); | |
| doSomething().then(doSomethingElse); | |
| /** | |
| * Весь смысл промисов в том, чтобы вернуть нам основы языка, потерянные в | |
| * момент нашего перехода на асинхронность: return, throw и стек. | |
| */ | |
| /** | |
| * Ошибка: Ад коллбеков -> Ад промисов. | |
| */ | |
| remotedb.allDocs({ | |
| include_docs: true, | |
| attachments: true | |
| }).then(function (result) { | |
| var docs = result.rows; | |
| docs.forEach(function(element) { | |
| localdb.put(element.doc) | |
| .then(function(response) { | |
| alert("Pulled doc with id " + element.doc._id + " and added to local db."); | |
| }) | |
| .catch(function (err) { | |
| if (err.status == 409) { | |
| localdb.get(element.doc._id) | |
| .then(function (resp) { | |
| localdb.remove(resp._id, resp._rev) | |
| .then(function (resp) {...}); | |
| }; | |
| } | |
| }; | |
| /** | |
| * Решение (composing promises): | |
| * Каждая последующая функция будет вызвана, когда предыдущий промис | |
| * «зарезолвится», и вызвана она будет с результатом работы | |
| * предыдущего промиса. | |
| */ | |
| remotedb.allDocs(...) | |
| .then(function (resultOfAllDocs) { | |
| return localdb.put(...); | |
| }) | |
| .then(function (resultOfPut) { | |
| return localdb.get(...); | |
| }) | |
| .then(function (resultOfGet) { | |
| return localdb.put(...); | |
| }) | |
| .catch(function (err) { | |
| console.log(err); | |
| }); | |
| /** | |
| * Ошибка: Как мне использовать forEach() с промисами? | |
| */ | |
| // Я хочу применить remove() ко всем документам: | |
| db.allDocs() | |
| .then(function (result) { | |
| result.rows.forEach(function (row) { | |
| // Метод remove возвращает promise | |
| db.remove(row.doc); | |
| }); | |
| }) | |
| .then(function () { | |
| // А здесь я наивно уверен, что все документы уже удалены! | |
| }); | |
| /** | |
| * Проблема в том, что первая функция возвращает undefined, а значит вторая | |
| * не ждет окончания выполнения db.remove() для всех документов. На самом | |
| * деле она вообще ничего не ждет и выполнится, когда будет удалено любое | |
| * число документов, а может и ни одного. | |
| * | |
| * Подводя итог, скажу, что конструкции типа forEach, for и while «не те дроны, | |
| * что вы ищете». Вам нужен Promise.all(): | |
| */ | |
| db.allDocs() | |
| .then(function (result) { | |
| var arrayOfPromises = result.rows.map(function (row) { | |
| return db.remove(row.doc); | |
| }); | |
| return Promise.all(arrayOfPromises); | |
| }) | |
| .then(function (arrayOfResults) { | |
| // Вот теперь все документы точно удалены! | |
| }); | |
| /** | |
| * Ошибка: Забываем добавлять .catch() | |
| */ | |
| somePromise().then(function () { | |
| return anotherPromise(); | |
| }) | |
| .then(function () { | |
| return yetAnotherPromise(); | |
| }) | |
| // простое и полезное окончание цепочки промисов: | |
| .catch(console.log.bind(console)); | |
| /** | |
| * Ошибка: использование внешних функций вместо возвращения результата. | |
| */ | |
| somePromise().then(function () { | |
| someOtherPromise(); | |
| }) | |
| .then(function () { | |
| // Я надеюсь someOtherPromise «зарезолвился»... | |
| // Осторожно, спойлер: нет, не «зарезолвился». | |
| }); | |
| /** | |
| * Каждый промис предоставляет вам метод then() (а еще catch()). | |
| * И вот мы внутри функции then(). | |
| * Внутри функции then() можно: | |
| * 1. Вернуть другой промис; | |
| * 2. Вернуть синхронное значение (или undefined); | |
| * 3. Выдать синхронную ошибку; | |
| */ | |
| /** | |
| * 1. Вернуть другой промис. | |
| */ | |
| getUserByName('nolan') | |
| .then(function (user) { | |
| // Функция getUserAccountById возвращает promise, | |
| // результат которого попадет в следующий then | |
| return getUserAccountById(user.id); | |
| }) | |
| .then(function (userAccount) { | |
| // Я знаю все о пользователе! | |
| }); | |
| /** | |
| * 2. Вернуть синхронное значение (или undefined). | |
| */ | |
| getUserByName('nolan').then(function (user) { | |
| if (inMemoryCache[user.id]) { | |
| // Данные этого пользователя уже есть, | |
| // возвращаем сразу | |
| return inMemoryCache[user.id]; | |
| } | |
| // А вот про этого пока не знаем, | |
| // вернем промис запроса | |
| return getUserAccountById(user.id); | |
| }) | |
| .then(function (userAccount) { | |
| // Я знаю все о пользователе! | |
| }); | |
| /** | |
| * 3. Выдавать синхронную ошибку. | |
| * | |
| * Это особенно удобно для отлавливания ошибок во время разработки. | |
| * Например, формирование объекта из строки при помощи JSON.parse() | |
| * где-либо внутри then() может выдать ошибку, если json невалидный. | |
| * С колбэками она будет «проглочена», но при помощи метода catch() | |
| * мы без труда сможем ее обработать. | |
| */ | |
| getUserByName('nolan') | |
| .then(function (user) { | |
| if (user.isLoggedOut()) { | |
| // Пользователь вышел — выдаем ошибку! | |
| throw new Error('user logged out!'); | |
| } | |
| if (inMemoryCache[user.id]) { | |
| // Данные этого пользователя уже есть, | |
| // возвращаем сразу | |
| return inMemoryCache[user.id]; | |
| } | |
| // А вот про этого пока не знаем, | |
| // вернем промис запроса | |
| return getUserAccountById(user.id); | |
| }) | |
| .then(function (userAccount) { | |
| // Я знаю все о пользователе! | |
| }) | |
| .catch(function (err) { | |
| // Упс, ошибка, но мы к ней готовы! | |
| }); | |
| /** | |
| * Ошибка: не знаем о Promise.resolve(). | |
| */ | |
| // Это: | |
| new Promise(function (resolve, reject) { | |
| resolve(someSynchronousValue); | |
| }) | |
| .then(/* ... */); | |
| // Можно заменить на: | |
| Promise.resolve(someSynchronousValue) | |
| .then(/* ... */); | |
| /** | |
| * Также такой подход очень удобен для отлавливания любых синхронных ошибок. | |
| * Любой код, который может выдать синхронную ошибку — потенциальная проблема | |
| * при отладке из-за «проглоченных» ошибок. | |
| * Но если обернуть его в Promise.resolve(), то можно поймать её | |
| * при помощи catch(). | |
| */ | |
| function somePromiseAPI() { | |
| return Promise.resolve() | |
| .then(function () { | |
| doSomethingThatMayThrow(); | |
| return 'foo'; | |
| }) | |
| .then(/* ... */); | |
| } | |
| /** | |
| * Ошибка: catch() не одно и то же с then(null, …). | |
| */ | |
| // Два примера ниже - равны. | |
| somePromise().catch(function (err) { | |
| // Обрабатываем ошибку | |
| }); | |
| somePromise().then(null, function (err) { | |
| // Обрабатываем ошибку | |
| }); | |
| // Два примера ниже - НЕ равны. | |
| somePromise() | |
| .then(function () { | |
| return someOtherPromise(); | |
| }) | |
| .catch(function (err) { | |
| // Обработка ошибка | |
| }); | |
| somePromise() | |
| .then(function () { | |
| return someOtherPromise(); | |
| }, function (err) { | |
| // Обработка ошибки | |
| }); | |
| /** | |
| * Если вы используете формат then(resolveHandler, rejectHandler), то | |
| * rejectHandler по факту не может поймать ошибку, возникшую внутри | |
| * функции resolveHandler. | |
| */ | |
| somePromise() | |
| .then(function () { | |
| throw new Error('oh noes'); | |
| }) | |
| .catch(function (err) { | |
| // Ошибка поймана! :) | |
| }); | |
| somePromise() | |
| .then(function () { | |
| throw new Error('oh noes'); | |
| }, function (err) { | |
| // Ошибка? Какая ошибка? O_o | |
| }); | |
| /** | |
| * Ошибка: промисы против фабрик промисов. | |
| */ | |
| function executeSequentially(promiseFactories) { | |
| var result = Promise.resolve(); | |
| promiseFactories.forEach(function (promiseFactory) { | |
| result = result.then(promiseFactory); | |
| }); | |
| return result; | |
| } | |
| function promiseFactory() { | |
| return somethingThatCreatesAPromise(); | |
| } | |
| /** | |
| * Ошибка: что, если я хочу результат двух промисов? | |
| */ | |
| function onGetUserAndUserAccount(user, userAccount) { | |
| return doSomething(user, userAccount); | |
| } | |
| function onGetUser(user) { | |
| return getUserAccountById(user.id) | |
| .then(function (userAccount) { | |
| return onGetUserAndUserAccount(user, userAccount); | |
| }); | |
| } | |
| getUserByName('nolan') | |
| .then(onGetUser) | |
| .then(function () { | |
| // К этому моменту функция doSomething() выполнилась, | |
| // а отступы не выросли — пирамидой и не пахнет | |
| }); | |
| // По мере того, как код будет усложняться, вы обратите внимание, | |
| // что все большая его часть преобразовывается в именованные функции, | |
| // а сама логика приложения начинает приобретать вид, приносящий | |
| // эстетическое удовольствие. Вот для чего нам промисы: | |
| putYourRightFootIn() | |
| .then(putYourRightFootOut) | |
| .then(putYourRightFootIn) | |
| .then(shakeItAllAbout); | |
| /** | |
| * Ошибка: Проваливание сквозь промисы. | |
| */ | |
| // Что выведет в консоль этот код? | |
| Promise.resolve('foo') | |
| .then(Promise.resolve('bar')) | |
| .then(function (result) { | |
| console.log(result); | |
| }); | |
| // Ответ: `foo`. | |
| // Причина в том, что, когда вы передаете в then() что-то отличное | |
| // от функции (например, промис), это интерпретируется как then(null) | |
| // и в следующий по цепочке промис «проваливается» результат предыдущего. | |
| // Проверьте сами. Добавьте сколько угодно then(null), | |
| // результат останется прежним — в консоли вы увидите `foo`. | |
| Promise.resolve('foo') | |
| .then(null) | |
| .then(function (result) { | |
| console.log(result); | |
| }); | |
| // Чтобы ожидания сбылись, нужно переписать пример как-то так: | |
| Promise.resolve('foo') | |
| .then(function () { | |
| return Promise.resolve('bar'); | |
| }) | |
| .then(function (result) { | |
| console.log(result); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment