LINUX.ORG.RU

асинхронность в js - способы написания

 , ,


1

1

О великие писатели, использующие JS! Подскажите стиль, на котором вы пишите асинхронные запросы!
async/await или на promise (then, catch)?
Почему?

ИМХО:
да, async/await похож на другие языки, но надо самому try/catch АшЫбоК.
Promise - цепочки выполнения, тоже красиво.

P.S. для затравки, как оно было: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

Ответ на: комментарий от Vit

Пхпхпх, нет.
В стандарте есть Callback queue и Job queue (microtask).
Обе пашут по FIFO. Пока что-то есть в очереди Job, callback не выполняется. А понты я и так могу поколотить. Показало, что код распарсить нормально не можем. Ввели promise, а в стандарт лезть никому не надо.

shleemypants
() автор топика
Ответ на: комментарий от shleemypants

Ты еще не учитываешь, что промис кто угодно может заменить кастомной имплементацией, а там по-моему нет требований, чтобы изменение состояния отражалось именно на nextTick или быстрее/медленнее.

Ну в общем, если хочешь искать в этом кокой-то смысл - дело хозяйское. Мне ближе прикладные моменты, и там подобную акробатику на ревью никто не пропустит.

Vit ★★★★★
()
Ответ на: комментарий от shleemypants

Я про сам JS и стандарт реализации асинхронщины.

Мне кажется ты загоняешься, асинхронщина на то и асинхронщина что порядок не определен, что на практике и получаем. В чем проблема то?

TDrive ★★★★★
()
Ответ на: комментарий от shleemypants

Дык это синтаксическая конструкция, в отличие от промиса. Нужна либо родная поддержка, либо транспилер, который весь твой код переколбасит.

Посмотри что бабель делает. Вроде его почти все дергают как один из этапов.

Vit ★★★★★
()
Ответ на: комментарий от drsm

async/await начинает рулить когда есть циклы асинхронные

Кстати, да. Сколько пишу асинхронщину — нихрена не могу понять, в каком месте мне понадобятся async/await и генераторы, когда есть промисы. Причем, даже async/await код в случае ветвления исполнения все равно приходит к тем же промисам (Promise.all()/allSettled()/race()/etc). А вот циклы на async/await и генераторах писать приятнее. Хотя, даже циклы я вполне реализовывал на рекурсии промисов.

Беглый поиск по NPM пакетам в том проекте, над которым я сейчас работаю, показал, что почти все генераторы — это попытка заменить async/await через __awaiter(function* ()...) или _asyncToGenerator(function* ()...). Разве что за исключением бабеля, у которого вообще все функции генераторные, в том числе

function* genTrue(data) {
  return true;
}
что есть бессмысленно и беспощадно — и таких в бабеле большинство.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Вот это поворот! Абизяна права! Да в моей теме! Что с этим миром?

shleemypants
() автор топика
Ответ на: комментарий от shleemypants

he said «I’ll be honest with you Jake, I’m not going to read that»

Вот это самая ценная часть статьи.

Какая разница что будет отрабатывать первым таймаут или промис? они асинхронные и мы должны ожидать любого порядка, и на практике мы так же получаем от них разный порядок выполнения.

TDrive ★★★★★
()
Последнее исправление: TDrive (всего исправлений: 1)
Ответ на: комментарий от shleemypants
Итого

Если нам нужно гарантировать выполнение какого-то кода после .then/catch/finally, то лучше всего добавить его вызов в цепочку .then.

Гениально.

TDrive ★★★★★
()
Ответ на: комментарий от shleemypants

Толку то от того что какая то конкретная реализация определяет порядок? На практике ни кто в здравом уме не будет тянуть в код подобную зависимость.

TDrive ★★★★★
()
Ответ на: комментарий от byko3y

С промисами вложенность распухает. Не так говёно как с колбеками, но тоже не айс. Когда бизнес-логику выписываешь, штуки вроде Promis.all нужны довольно редко. В основном - дернуть тонны чего-то асинхронного, и условия с if и for. А тут код с await будет намного легче читаться.

Vit ★★★★★
()
Ответ на: комментарий от TDrive

он не объяснил, почему. Но показывает ошибки. и ссылку с микро на макро ;)

shleemypants
() автор топика

Всем спасибо за содержательные ответы.

shleemypants
() автор топика
Ответ на: комментарий от Vit

С промисами вложенность распухает. Не так говёно как с колбеками, но тоже не айс

Вложенность «распухает» ровно на один уровень. Код линейный, просто вместо await xxx(param); приходится писать .then((param) => xxx(param))

В основном - дернуть тонны чего-то асинхронного, и условия с if и for

Да, циклы действительно удобно делать на async/await, но на самом деле в браузерередко возникает потребность выполнить строго фиксированное число итераций асинхронных вызовов. Зачастую, как ты правильно указал, продолжение итераций условное и for непонятно по какому итератору делать. А как я правильно написал, при известном числе итераций браузер создает асинхронные запросы сразу скопом и ждет их окончания, потому что в свете HTTP/2 ждать круговую задержку после каждого запроса — это очень неэффективно.

То есть, в браузерах у async/await ниша есть, но очень специфичная. А вот у генераторов ниши вообще никакой нет с появлением async/await.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Код линейный, просто вместо await xxx(param); приходится писать .then((param) => xxx(param))

При желании можно делать then внутри then, почти как калбэки получится.

Nervous ★★★★★
()
Ответ на: комментарий от Nervous

При желании можно делать then внутри then, почти как калбэки получится

then внутри then нужно делать только в случае нелинейного потока выполнения. Причем, в таком случае код с async/await получится не менее страшный, поскольку упрощает он только линейное выполнение.

byko3y ★★★★
()
Ответ на: комментарий от Vit

вот прям в соседних предложениях

штуки вроде Promisе.all нужны довольно редко
В основном - дернуть тонны чего-то асинхронного, и условия с if и for

Princesska ★★★★
()
Ответ на: комментарий от TDrive

Микротаски и макротаски тянутся не из спеки ECMAScript, по которой порядок действительно недетерминирован, а из спеки HTML5, которая переопределяет некоторые части стандарта ECMAScript: https://html.spec.whatwg.org/#event-loops

Ну а поскольку практически все практически используемые движки JS взяты из браузеров, то и разработчики полагаются на данный порядок.

static_lab ★★★★★
()

Наши фронты сто лет назад на async/await переехали.

theNamelessOne ★★★★★
()
Ответ на: комментарий от shleemypants

это такая монада Maybe

Но не стоит забывать что у промисов есть прикольные фишки

Монада — это примерно то же самое, что и буррито.

theNamelessOne ★★★★★
()
Ответ на: комментарий от romanlinux

Не удивлюсь, если тот же V8 у себя унутре все эти ваши промисы/асинки оптимизирует в те же колбэки.

theNamelessOne ★★★★★
()

Естественно надо на async/await писать, для этого его и придумали. Под капотом-то всё равно будет promise, но читать в тыщу раз проще.

Legioner ★★★★★
()
Ответ на: комментарий от Nervous

Ах ты ж мамкин лиспер

А ты проницательный! (нет)

pihter ★★★★★
()
Ответ на: комментарий от romanlinux

В плане производительности, лучше всего будет callback

Вроде как это уже починили.

crutch_master ★★★★★
()
Ответ на: комментарий от byko3y

Вложенность «распухает» ровно на один уровень. Код линейный, просто вместо await xxx(param); приходится писать .then((param) => xxx(param))

У весь тебя код из 1 строчки и после каждого промиса происходит вызов функции? Обычно бывает, что

let foo = await xxx();
//что-то сделать с foo
let bar = await yyy();
//что-то сделать с bar
//etc

crutch_master ★★★★★
()
Ответ на: комментарий от splinter
const data = await getData();
if (data) { ... }
против 
getData().then((res) => {
...
})

js это унылость и безысходность.

это какая-то дичь.

но забавно горит, при отсутствии понимания, от таких эффектов:

try {
    const p1 = some1();
    const p2 = some2();
    
    const r1 = await p1;
    return r1.wtf(await p2); 
} catch (e) {
    // handle it
    return null;
}
// уходим в нирвану по UnhandledPromiseRejection
drsm ★★
()
Ответ на: комментарий от static_lab

Ну а поскольку практически все практически используемые движки JS взяты из браузеров, то и разработчики полагаются на данный порядок.

Есть пример в котором разработчику сайта нужно понимать в каком порядке будут выполняться асинхронные таски?

TDrive ★★★★★
()
Ответ на: комментарий от TDrive

Есть пример в котором разработчику сайта нужно понимать в каком порядке будут выполняться асинхронные таски?

Лорчую. Известно, что на порядок полагаться нельзя, значит, не надо полагаться.

Nervous ★★★★★
()
Ответ на: комментарий от byko3y

Вложенность «распухает» ровно на один уровень.

  1. Обертка даже одного уровня заметно загромождает место.
  2. На практике довольно часто приходится возвращать промис из подвызова (следствие необходимости «if»), вложенность больше единицы. for нужен относительно редко, if - часто.

Ну в общем, IMO обычный код на промисах есть смысл фигачить только ради легаси, когда неохота с транспилерами связываться.

Под ноду я строго на async/await пилю. Под браузеры - по обстоятельствам.

А вот у генераторов ниши вообще никакой нет с появлением async/await.

Ну они действительно не для бизнес-логики. На них всякие transform-стримы можно конструировать, и iterable для for-of. Системная штука для всяких библиотечных подвывертов.

Vit ★★★★★
()
Ответ на: комментарий от byko3y

Подавляющее большинство ситуаций с if & for можно написать «условно линейно», если использовать много return (и без else):

if (condition1) return xx;

if (condition2) return yy;

if (condition1) return zz;

...

Ну смысл в том, что пока не надо скакать взглядом по коду, а просто ведешь вниз, то воспринимается как линейный код.

Vit ★★★★★
()
Последнее исправление: Vit (всего исправлений: 1)
Ответ на: комментарий от TDrive

Есть пример в котором разработчику сайта нужно понимать в каком порядке будут выполняться асинхронные таски?

Очевидно, чтобы собеседование пройти, потому что разработчики начитаются умных статеек, и начинают задавать дурацкие вопросы, например, что выстрелит раньше: Promise.then или setTimeout.

А на практике особой гонки между макро- и микротасками никто и не устраивает вроде бы.

static_lab ★★★★★
()
Ответ на: комментарий от static_lab

пхпхпхпх, а понимать, как оно работает надо? Или тяп-ляп, а потом дебажим-дебажим, насяльника зовём? А гонки нет. В спеке: сначала queue job(micro), потом уже остальные. Если у меня будет loop бесконечный на promise, то хрен ты увидишь свой setTimeout.

shleemypants
() автор топика
Последнее исправление: shleemypants (всего исправлений: 1)
Ответ на: комментарий от TDrive

Пхпхпх, любой, где бесконечно добавляется promise. Ещё раз, пока promise очередь не будет чиста, event-loop не даст запуск callback. В браузерах и ноде только в отпределённых старых версиях была UB реализация. Теперь фсё чётка.

В статье выше годные примеры, есть для пацанов норм видосы, а не «от Васяна», который заливает в аудиторию хрен пойми что.

shleemypants
() автор топика
Последнее исправление: shleemypants (всего исправлений: 2)
Ответ на: комментарий от shleemypants

Ты не понимаешь того, когда именно job оказывается в queue. Таск от промиса падает в рчередь на выполнение, не тогда клгда ты этот промис создал, а когла он перешео в состоянение rejected или resolved, и только тогда. И отсюда и берется race. В частности твой пример с фетчем - таск в очередь палает, когда полусен ответ от сисиемы ввода/вывода и промис перешел из состояния pending в одно из готовых к выполнению. А пока это произойдет цикл эвентлупа модет успеть прокрутится миллионы раз, потому что ввод вывод недетерминирован.

javascript
()
Ответ на: комментарий от shleemypants

любой, где бесконечно добавляется promise.

Так это и без промиса можно

console.log("start")
setTimeout(() => console.log("test"), 1000)
while (true);
TDrive ★★★★★
()
Ответ на: комментарий от crutch_master

У весь тебя код из 1 строчки и после каждого промиса происходит вызов функции? Обычно бывает, что

Твоему коду вообще не нужен async/await, потому что await-ы независимы:

let [foo, bar] = await Promise.all([xxx(), yyy()]);
// делаем что-то с foo и bar

byko3y ★★★★
()
Ответ на: комментарий от byko3y

это другое :), ты в паралель запустил, см выше мой пример, бывают еще как зависимы ).

drsm ★★
()
Ответ на: комментарий от drsm

но забавно горит, при отсутствии понимания, от таких эффектов

Слушай, а у меня погорело. Я в упор не мог понять, откуда тут UnhandledPromiseRejection. Оказывается, это волшебные особенности реализации обработки исключений в async у ECMAScript. Создатели как бы поразумевали, что ты можешь делать только

try {
    const r1 = await some1();
    return r1.wtf(await some2()); 
} catch (e) {
    // handle it
    return null;
}

А в случае заранее заготовленных промисов таки сахар просыпается и приходится его собирать руками, потому что не await-ченый промис оказывается без обработчика catch, из-за чего и выдается UnhandledPromiseRejection в тот момент, когда главный цикл V8 доходит до обработки этого промиса.

byko3y ★★★★
()
Ответ на: комментарий от Vit

Подавляющее большинство ситуаций с if & for можно написать «условно линейно», если использовать много return (и без else):
if (condition1) return xx;
Ну смысл в том, что пока не надо скакать взглядом по коду, а просто ведешь вниз, то воспринимается как линейный код

Всё, до меня дошло. Действительно, код с таким большим ветвлением на промисах придется во много уровней писать.

byko3y ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.