Комментарии 53
Но на мой взгляд это не повод опускать руки. Скорость с которой браузеры вводят поддержку новых фич, не говоря уже о ES2015, заставит разработчиков, подобного рода инструментов, подтянуть свои продукты под современные реалии. Иначе придет кто-то новый, более адаптированный.
Например у Uglifyjs есть экспериментальная ветка harmony, как раз нацеленная на поддержку ES6.
В данный момент, я решил пробовать разработку без babel на небольших внутренних проектах. Где можно принебречь некоторыми вещами, например Uglifyjs.
function authenticateUser(login, password) {} //возвращает Promise<userId> при успехе
function getUserDetails(userId) {}
function getUserAvatar(userId) {}
И я хочу запустить authenticateUser, а по его завершению — getUserDetails и getUserAvatar параллельно.
Через `then` это делается элементарно, включая обработку ошибок — достаточно проверить результирующий промис:
let detailsAndAvatar = authenticateUser(login, password).then(function(userId) {
return Promise.all(getUserDetails(userId), getUserAvatar(userId));
});
Как это будет выглядеть на async/await?
async f() {
const userId = await authenticateUser(login, password);
return await Promise.all([getUserDetails(userId), getUserAvatar(userId)]);
}
f -> f1
|-> f11
|-> f12
-> f2
|-> f21
|-> f22
Простите за снобство, но это плохой пример. Если async f()
возвращает обещание, то зачем оператор await
после return
? Не достаточно ли будет "вернуть" обещание? Механизм Promise'ов ждёт обещания любой глубины. Даже если обещание resolve'ит другое обещание.
На сопрограммах (node-fibers) это будет выглядеть так:
let userId = authenticateUser( login , password ).wait()
let detailsFuture = getUserDetails( userId )
let avatarFuture = getUserAvatar( userId )
let detailsAndAvatar = [ detailsFuture.wait() , avatarFuture.wait() ]
А с моим велосипедом, вообще вот так:
let userId = authenticateUser( login , password )
let details = getUserDetails( userId )
let avatar = getUserAvatar( userId )
let detailsAndAvatar = [ details , avatar ]
Вы какую-то слишком общую ссылку привели. Лучше сразу на примеры с велосипедом. По какому принципу это работает? В каком контексте? Нужно ли оборачивать эти методы чем-нибудь? Нужно ли на вершине стека какой-нибудь Fiber(context) запускать?
А самое интересное, чего я пока не понял до конца, это в чём различия подхода волокон и async-await? Я так понимаю, и там и там, не создаётся новых потоков, а только переключаются стеки состояний, что дешевле, чем, скажем, новые потоки, но тем не менее далеко не бесплатно. Или нет? Вы не могли бы объяснить в двух словах?
Ок, вот более конкретная ссылка. Да, всё приложение нужно стартовать в волокне и всё.
async-await — это те же генераторы, которые не имеют стека. Просто машина состояний. А волокна — это такие себе легковесные потоки, каждый со своим стеком. В случае генераторов мы дополнительно платим за каждый вызов функции. В случае волокон — платим лишь за переключение волокон.
Речь об этой статье? Там фактические не волокна, а эмуляция async-await через генераторы.
Только сейчас могу сформулировать, чем же мне не нравится async/await. Да тем, что он провоцирует программистов писать последовательный код! Асинхронный, но все операции выполняются по очереди, лишая смысла основную идею — параллельные программы.
С другой стороны, код на промисах "по умолчанию" полностью параллельный, и последовательность операций определяется неявно, через вычислительные зависимости, выраженные в then
пример: сложное приложение, делающее множество Ajax вызовов, на промисах будет по возможности делать эти вызовы параллельно. На async/await… для этого придется прикладывать усилия.
Цепочка промисов через then выполняется последовательно, параллельным является Promise.all
, например. И он прекрасно сочетается с async/await
, например,:
await Promise.all([ajax1, ajax2])
Можете привести пример кода из "сложного Ajax приложения, делающего вызовы по возможности параллельно"? Я не понимаю за счёт чего оно внезапно станет "по-умолчанию" параллельным на промисах?
Следите за руками:
require( 'jin' ).application( function( $ ){
function get( ){
return $.request.getSync( "http://example.org/?" + Math.random() )
}
console.time( 'serial' )
console.log( get().statusCode )
console.log( get().statusCode )
console.timeEnd( 'serial' )
console.time( 'parallel' )
var resp1= get()
var resp2= get()
console.log( resp1.statusCode )
console.log( resp2.statusCode )
console.timeEnd( 'parallel' )
} )
200
200
serial: 2418ms
200
200
parallel: 1189ms
т.е. get() асинхронный, но поток выполнения блокируется при вызове любого метода на результате?
это же лочит браузер?
Кстати, в redux-saga используются генераторы, но на первый взгляд работают точно таким же образом (я не говорю про то как там внутри реализовано).
А разве Promise-ы в целом не работают через try-catch?
new Promise((r,j) => { throw 1; }).catch(err => console.log('error=', err))
// error= 1
Т.е. это скорее общая особенность работы Promise
, нежели особенность await
. Проигрываем в оптимизации, зато получаем проброс ошибок.
function testFunction() {
new Promise((r,j) => { throw 1; }).catch(err => console.log('error=', err))
}
Function is optimized
Добавление в любом месте try-catch (даже пустого) приводит к:
Function is not optimized
У вас testFunction
is optimized, а внутри исходников promise
, где-нибудь есть метод, внутри которого стоит try-catch
. И вот там будет is not optimized
. Полагаю, что async
-методы в первое время будут, что с try-catch
, что без него, не оптимизированными, а уже апосля руки дойдут и до них. Во всяком случае, смотря на список убийц оптимизации у меня сложилось впечатление, что нужно писать код стоя на одной ноге на ципочках, чтобы не выпасть из оптимизации. Шаг в лево, шаг в право — приехали.
Для теоретического async-await пока вариант явно отказаться либо от ловли ошибок, либо от оптимизаций (интересно как с этим дела в Edge). Может потому и не спешат внедрять
Кстати, в V8 уже оптимизировали некоторые моменты из числа убийц:
- функции, содержащие выражение for-of;
- функции, содержащие составной оператор присваивания let;
- функции, содержащие составной оператор присваивания const;
- функции, содержащие объектные литералы, которые, в свою очередь, содержат объявления __proto__, get или set.
- бесконечные циклы
- 5.1. Ключ не является локальной переменной (первый пример с nonLocalKey1)
- частично 5.2. Итерируемый объект не является «простым перечисляемым»
- может что-то еще, протестировал только эти
Function is optimized by TurboFan
И вполне возможно скоро можно будет не так активно задумываться над убийцами оптимизаций
еще есть асинхронные генераторы. Пример синтаксиса:
async function* myFunction() {
await yield new Promise((resolve) => {});
}
вот тут подробнее https://www.youtube.com/watch?v=DqMFX91ToLw
Ваша информация сильно устарела. На текущий момент, асинхронная итерация и Observable разделены.
Но, если async function возвращает Promise, тогда, наверное, можно и так:
async function unicorn() {
let rainbow = await getRainbow();
return rainbow.data.colors;
}
unicorn()
.catch(function(error) {
...
});
Async/Await в javascript. Взгляд со стороны