Комментарии 28
Вот вы все только обещаете...
А если серьезно, то спасибо) Вот вроде все понятно, но когда приходится иногда написать что-то подобное, то сразу появляется "две проблемы"©
Обратите внимание на то, что каждая цепочка обещаний заканчивается вызовом catch(). Если этого не делать, то возникающие ошибки будут пропадать в недрах обещаний и, тогда невозможно будет установить, где произошла ошибка.
Неправда, нигде они не теряются и вполне их можно захендлить
window.addEventListener('unhandledrejection', e => {
console.log('handle', e);
return e.preventDefault();
});
Если обработчика не будет, то промис просто кинет exception, который можно увидеть в консоли и принять меры.
А в Хроме можно и без события неперехваченные ошибки в консоли увидеть.
Вот на ноде-то с этим как раз проще, так как там нет зоопарка неподдерживающих это браузеров.
я написал для этого обработчик, который роняет процесс nodejs, т.к. имхо необработанный reject == исключение.
Предположим, нам нужно выполнить какое-либо асинхронное действие для каждого элемента массива array. Пусть это асинхронное действие обернуто в некую функцию doSomething(), которая возвращает обещание выполнить это асинхронное действие. Шаблон того, как в этом случае выстроить цепочку обещаний, как это ни странно, основан не на использовании функции forEach, а на использовании функции reduce
А разве не?
...
Promise
.all(arr.map((item) => doSomething(item)))
.then( ... );
Ваш способ запустит все асинхронные действия одновременно, а не последовательно.
Как же в цикле последовательно выполнить обещания? [...]
Перейдем к рассмотрению самих механизмов цикличного построения цепочек обещаний. Будем отталкиваться от привычных всем программистам разновидностей циклов: цикл for и цикл forEach.
// Повторное разрешение того же обещания promise.then(function(result){ // Теперь уже асинхронный вызов не выполняется. // В эту функцию передается сохраненный результат // разрешения обещания });
Асинхронный вызов еще как выполняется.
Какой executor? Если вы имеете в виду тот, который вернул promise — то он первый раз не выполнялся. Потому что выполнился раньше.
executor
A function that is passed the arguments resolve and reject. The executor function is executed immediately by the Promise implementation, passing resolve and reject functions (the executor is called before the Promise constructor even returns the created object). The resolve and reject functions, when called, resolve or reject the promise respectively. The executor normally initiates some asynchronous work and then, once that completes, calls either the resolve or reject function to resolve the promise or else reject it if an error occurred.
Да, он выполняется при создании промиса не зависимо от того, сколько раз у него вызывался метод then() (даже если 0 раз). Думаю, это хотел сказать автор, просто выразил свою мысль некорректно. Executor кстати не асинхронно запускается, другое дело, что внутри него как правило есть асинхронный код. А в целом, все эти моменты легко понять, если на вывод этого кода внимательно посмотреть:
p = new Promise(r => { console.log('executor'); r(); });
console.log('after creation');
p.then(() => console.log('in chain 1')).then(() => console.log('in chain 2'));
p.then(() => console.log('in chain 3'));
console.log('after chaining');
// Первое разрешение обещания
promise.then(function(result){
// При разрешении обещания происходит асинхронный вызов,
// результат которого передается в эту функцию
});
// Повторное разрешение того же обещания
promise.then(function(result){
// Теперь уже асинхронный вызов не выполняется.
// В эту функцию передается сохраненный результат
// разрешения обещания
});
Не верно это.
Получение результата промиса происходит во время синхронной инициализации самого промиса:
console.log('1');
const promise = new Promise(resolve => {
resolve('result');
console.log('2');
});
console.log('3');
/*
В консоль выведется 1,2,3, а promise уже содержит готовый результат 'result'
*/
Вызов разрешающей функции then
вообще не нужен для получения и сохранения результата внутри промиса, т.к результат всегда получается по время инициализации самого промиса. then
нужен только для "извлечения" этого результата
promise.then(function (result){
// success
}, function (err) {
// error
});
Это имеет смысл, когда у вас есть вложенные promise, например:
promise1.then(function(result){
// success promise1
return promise2.then(function() {
// success promise2
});
}, function (err) {
// error promise1
}).catch(function(err) {
// error promise2
});
И у вас одновременно запускается два обработчика
function processRequest(req, res){
requestQueue = requestQueue.then(function(){
...
});
}
И запросы пришли настолько одновременно, что при вычислении requestQueue в правой части они оба получили начальное состояние Promise.resolve() и, соответственно, выполнятся не последовательно. Или нам кто-то (стандарт или движок ecmascript/javascript?) гарантирует атомарность такой операции? Мне кажется, что нет.
Но даже если да, то в нашем случае получается что сам веб-сервер превращается в синхронный, что означает бессмысленность использования Promise.
Так как же бороться за атомарность получения И присвоения значения переменной в случае асинхронных вызовов? Я уж надеюсь, что стандарт нам хотя бы гарантирует атомарность присвоения значения, верно?
Другое дело генераторы.
sync(getData('habrahabr.ru', 'js@generator.com'))((data) => console.log(data))
*getData(url, email) {
const password = yield getPassword(email) // return function(callback) { Account.findOne({email: email}, (error, data) => { callback(data.password) })
const result = yield sync(request(url, email, password))
if (result.error) new Error(result.error)
return result.data
}
//Обвертка для вызова ген-фу
function sync(iterator) {
let job = iterator.next()
let done = () => {}
const next = () => {
if (typeof(job.value) == "function") {
job.value(function(val) {
job = iterator.next(val)
if (!job.done) next()
else done(job.value)
})
} else done(job.value)
}
return (callback) => {
done = callback
next()
}
}
Еще раз про обещания