Skip to content

Instantly share code, notes, and snippets.

@YozhEzhi
Created July 18, 2018 08:13
Show Gist options
  • Select an option

  • Save YozhEzhi/4196331d5e7aacbf721f06ec6bc18fd0 to your computer and use it in GitHub Desktop.

Select an option

Save YozhEzhi/4196331d5e7aacbf721f06ec6bc18fd0 to your computer and use it in GitHub Desktop.
Promises.
/**
* Задачки на проверку работы промисов.
*/
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