Как стать автором
Обновить

Комментарии 53

Синтаксиса await* не будет так как они говорят что есть уже Promise.all. Это не одно и тоже что в yield и yiled*
Привет, xGromMx. А можно ознакомиться где это написано или сказано? Заранее спасибо.
НЛО прилетело и опубликовало эту надпись здесь
Спасибо, degorov. Ознакомлюсь и внесу соответствующие корректировки.
Глянь это ишью https://github.com/tc39/ecmascript-asyncawait/issues/85
А как сейчас можно отказаться от Babel, если нужна минификация для продакшена? Я недавно думал отключить Babel на одном проекте, но потом понял, что Uglifyjs не поддерживает нормально ES6, и оставил все, как есть.
Попробуйте меньше копипастить и минификация вам не понадобится ;-)
arusakov, Я понимаю что с места этот барьер будет тяжело взять. Я к этому не призываю. И сам недавно столкнулся с похожей проблемой.
Но на мой взгляд это не повод опускать руки. Скорость с которой браузеры вводят поддержку новых фич, не говоря уже о ES2015, заставит разработчиков, подобного рода инструментов, подтянуть свои продукты под современные реалии. Иначе придет кто-то новый, более адаптированный.
Например у Uglifyjs есть экспериментальная ветка harmony, как раз нацеленная на поддержку ES6.

В данный момент, я решил пробовать разработку без babel на небольших внутренних проектах. Где можно принебречь некоторыми вещами, например Uglifyjs.
Частично с минификацией справляется closure compiler
При чём тут минификация и Babel?
Исходя из того, что компилирует babel, await перестает быть асинхронным и поток вполне себе блокируется. Так что это то еще зло, если не используется в отдельном воркере.
Где вы там блокировку увидели? while(1) в regeneratorRuntime используется для того чтоб перезапускать генераторы с циклами, ничего он не блокирует.
НЛО прилетело и опубликовало эту надпись здесь
Эм, пруфы, пожалуйста. Babel переводит async/await либо в генераторы, либо использует регенератор.
Да-да, теперь понял, ошибался.
Для меня это выглядит так, как будто async/await бесполезен в реальных проектах. Допустим, у нас есть 3 функции:
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'ит другое обещание.

это и есть проблемы промисов у них map и flatMap ведут себя одинаково хотя было бы правильно с точки зрения функтора и монад так
const pf = Promise.of(42).map(v => v + 10);
const pm = Promise.of(42).flatMap(v => Promise.of(v + 10).delay(1000)) // псевдокод с delay
За что минусы?

Promise.of нет в спецификации

А xGromMx и не утверждал обратного. Он просто привёл пример, того как, по его мнению, было бы правильнее. Код напоминает scala.

где обертка в async?)
Грубо говоря async/await есть do монада

На сопрограммах (node-fibers) это будет выглядеть так:


let userId = authenticateUser( login , password ).wait()
let detailsFuture = getUserDetails( userId )
let avatarFuture = getUserAvatar( userId )
let detailsAndAvatar = [ detailsFuture.wait() , avatarFuture.wait() ]

Вы какую-то слишком общую ссылку привели. Лучше сразу на примеры с велосипедом. По какому принципу это работает? В каком контексте? Нужно ли оборачивать эти методы чем-нибудь? Нужно ли на вершине стека какой-нибудь Fiber(context) запускать?


А самое интересное, чего я пока не понял до конца, это в чём различия подхода волокон и async-await? Я так понимаю, и там и там, не создаётся новых потоков, а только переключаются стеки состояний, что дешевле, чем, скажем, новые потоки, но тем не менее далеко не бесплатно. Или нет? Вы не могли бы объяснить в двух словах?

Ок, вот более конкретная ссылка. Да, всё приложение нужно стартовать в волокне и всё.


async-await — это те же генераторы, которые не имеют стека. Просто машина состояний. А волокна — это такие себе легковесные потоки, каждый со своим стеком. В случае генераторов мы дополнительно платим за каждый вызов функции. В случае волокон — платим лишь за переключение волокон.

Скинь ссылку на статью про волокна, я помню у тебя где-то было на github

Речь об этой статье? Там фактические не волокна, а эмуляция 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() асинхронный, но поток выполнения блокируется при вызове любого метода на результате?
это же лочит браузер?

В браузере это и не работает. Только в ноде. Лочится не весь процесс, а отдельный стек вызовов, который запускает application. Вот тут действительно по возможности распараллеливается работа.

То есть, если я все правильно понимаю — отловить ошибки в async/await функциях можно только через try/catch? Учитывая не очень хорошую производительность try/catch, мне кажется что это огромный минус таких функций.
Кстати, в redux-saga используются генераторы, но на первый взгляд работают точно таким же образом (я не говорю про то как там внутри реализовано).
GeraldIstar, да, все верно. Не очень хороша производительность, а так же V8 не оптимизирует функции, содержащие эту конструкцию
Ошибаетесь. :)

Turbofan нынче отлично оптимизирует try-catch-finally, for..of и прочие конструкции, которые ранее считались убийцами оптимизации (см. http://benediktmeurer.de/2017/04/03/v8-behind-the-scenes-march-edition/)

Все развивается ;)

А разве 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, что без него, не оптимизированными, а уже апосля руки дойдут и до них. Во всяком случае, смотря на список убийц оптимизации у меня сложилось впечатление, что нужно писать код стоя на одной ноге на ципочках, чтобы не выпасть из оптимизации. Шаг в лево, шаг в право — приехали.

Вполне может быть, в реализации промисов Bluebird от Petka Antonov, которые быстрее нативных, как раз используется изолированная функция tryCatch для catch, использование которой не мешает V8 оптимизировать весь остальной ваш код

Для теоретического async-await пока вариант явно отказаться либо от ловли ошибок, либо от оптимизаций (интересно как с этим дела в Edge). Может потому и не спешат внедрять

Кстати, в V8 уже оптимизировали некоторые моменты из числа убийц:
  • функции, содержащие выражение for-of;
  • функции, содержащие составной оператор присваивания let;
  • функции, содержащие составной оператор присваивания const;
  • функции, содержащие объектные литералы, которые, в свою очередь, содержат объявления __proto__, get или set.
  • бесконечные циклы
  • 5.1. Ключ не является локальной переменной (первый пример с nonLocalKey1)
  • частично 5.2. Итерируемый объект не является «простым перечисляемым»
  • может что-то еще, протестировал только эти

Function is optimized by TurboFan

И вполне возможно скоро можно будет не так активно задумываться над убийцами оптимизаций
НЛО прилетело и опубликовало эту надпись здесь
"… Использование try/catch это единственный способ поймать и обработать ошибку...".

Но, если async function возвращает Promise, тогда, наверное, можно и так:

async function unicorn() {
    let rainbow = await getRainbow();
    return rainbow.data.colors;
}

unicorn()
  .catch(function(error) {
     ...
  });
  
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации