Pull to refresh

Comments 26

Сложность с async/await у меня одна: он вирусный. Всё начинается с одной функции, но очень скоро оно вынуждает преобразовать буквально каждую функцию приложения в async, и пихать await перед каждым запросом данных. Решения этой проблемы глобально я не знаю. Можно, конечно, городить разворачивание промисов, если те передаются в качестве аргументов другим функциям, но SRP при этом сразу идёт лесом, т.к. туда же придётся перевезти часть управляющих конструкций, зависящих от результата асинхронной операции.

Обычно превращается во что-то такое (пример из головы, не обязан компилиться)
const inv: Inventory = await Inventories.create({ id: 'efrvukij' }, { persistent: false });
const bread = await inv.items.create({ name: 'bread', amount: 4});
const foundWater = await inv.items.findOne({ name: 'water' });
const len = await inv.items.count();
if (len > 3 && foundWater) {
  if (await foundWater.delete()) {
    UI.Notify(`It's done!`)
  }
}

Ехал await через await. И это только логика, без обработки исключений при работе с БД, например, которые могут случиться хоть в каждой из функций враппера выше.

Не вижу ничего плохого в приведенном примере, с колбеками или прямая работа с промисами выглядела бы куда хуже

Ничего радикально плохого там и нет, тем более с подобным кодом и приходится чаще всего работать. Просто чувство прекрасного негодует и говорит, что потенциально должен быть синтаксис лучше.

Ну так это плата за корутины без стека.

Либо у нас stackless и растём снизу вверх либо мы сверху вниз сохраняя стек (stackful).

Производительнее для модели JS стек не сохранять.

Можно параллельно и без гибридного подхода...

a = getPromise();
b = getPromise();

await a;
await b;

Обе функции начнуттсвою асинронную работу сразу. Даже если b закончит раньше, дождёмся a потом сразу(черезтитерацию лупа) b. Это полностью эквивалентно promice all

Понял, я почему-то после Python думал, что пока явно не добавить Promise в event_loop, он выполняться не будет

Два исключения сразу в a и в b при таком подходе рискуют уронить всю программу (если это нода) или некрасиво "выплыть" в консоли (если дело в браузере).

не роняет

image


… но пачкается, да

Тимофей Тиунов: “В данном примере async не нужен. Вот пример, где как бы есть await, но в конце возвращается promise:

Полагаю, что в примере, к которому написано это замечание, могут быть асинхронные вопросы, результат которых возвращается, например, что-то вроде этого (пример условный):

async function loadData(id) {
  let response;
  
  try {
    response = await fetch(`/data/${id}`);
  } catch (e) {
    console.error('request failed by the reason:', e);
    return null;
  }
  
  return response;
}

Эта функция возвращает Promise, хотя сам по себе объект response таковым и не является.

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

Согласен. Практика показывает, что если функция возвращает Promise, она всегда должна быть помечена как async. Это и нагляднее, и убережет от случая когда функция будет изменена и вместо Promise будет возвращено обычное значение, к чему клиентский код может быть не готов.

… или просто возьмите Typescript.

Здравый смысл мешает. Нет нужды городить лишнюю абстрацию поверх вызова promise, если проблемы с "клиентский код может быть не готов" не существует как класс.

async перед именем функции - на лишнюю абстракцию не тянет. Было бы интересно услышать доводы против этого (как для JS так и в TS).

async перед именем функции это не чёрная магия. Это runtime. По сути:


function a() {
    return new Promise((resolve, reject) => {
        Promise.resolve(1).then(resolve, reject);
    });
}

async function b() {
  return await Promise.resolve(1); // либо без await
}

Это плюс минус одно и то же. async тут просто сахар. А можно просто так:


function c() {
    return Promise.resolve(1);
}

С практической точки зрения разница (a и c):


  • могут быть отличия в stack traces
  • немного лишнего runtime т.к. создаётся лишний сегмент promise-матрёшки со всеми вытекающими. Promise сам по себе тяжёлая абстракция, а async-await втройне (там ведь finite machine)
  • в non-async функции случайно не напишешь await
  • некоторые тонкости в обработке ошибок (это камень в сторону варианта c)
  • в случае какой-нибудь утиной типизации нельзя вернуть не Promise (хотя ну её эту утиную типизацию, если честно)
  • полагаю на уровне lib-uv можно поймать приколы вроде разной обработки очереди (но тут надо вникать, мне лень)

Спасибо, список внушительный. Оверхед, пожалуй главный аргумент. Сравнил два варанта, без async выходит в полтора раза быстрее.

Я конечно душнила ещё тот. Но статьи пережевывающие одну и ту же тему уже просто в глазах мельтешат. Реферат за рефератом, это похоже на то что люди просто себе делают заметку в процессе обучения, в виде статьи на хабре. Оригинальные темы, исследования, гипотезы уже исчерпали себя. Только хардкорная перепечатка давно известных тем. Копни Хабр поглубже найдётся статей 5 про as/aw. Копни медиум, индусы насыпят ещё 50. Мне что-то это напомнило систему в университете, когда у научного сотрудника оклад может зависить от цитируемости. Теперь это ушло в hh плоскость, потенциальные сотрудники стараются увеличить объём публикаций в надежде на строчечку в резюме: публикации - хабр.

Представим, что нам нужно выполнить операцию с большим набором данных для книжного магазина. Наша задача — найти всех авторов, написавших более 10 книг, в нашем наборе данных и вернуть их биографические данные.

это один запрос с join, count в базу. Не стоит разбивать на задачи и обрабатывать на беке

Абсолютно верно, тот самый случай, когда важно поставить правильную задачу, а не вот это вот "10 запросов на бекенд", хотя случаи всякие бывают, конечно. Думаю, именно из-за подобных проблем и родился GraphQL.

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

..представим, что надо найти всех авторов, проживающих не более чем в 10 часах езды на автомобиле от указанной точки. Расчёт выполняется через Яндекс.Карты...

Не для всех задач все данные удобно лежат на собственном сервере.

С async/await асинхронный код выглядит, как обычный синхронный код. Это позволяет записать больше логики в одном методе, но также кроет в себе опасность, особенно для начинающих разработчиков. Из-за этого они плохо понимают, как работает асинхронность, и просто ставят async/await везде, где только можно.

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

У меня есть видео-урок по асинхронности, там более подробно об этом рассказано: https://youtu.be/p0d8p9C2aYs

async и await — это синтаксический сахар поверх Promise, который позволяет писать асинхронный код так, как будто инструкции выполняются сверху вниз одна за другой, местами значительно упрощает структуру кода и повышает читаемость. 

async и await не является синтаксическим сахаром к Promise. Это синтаксический сахар к генератору, который каждый yield оборачивает в Promise.

Иначе говоря код с async awaite точно так же переписывается используя генераторы, без использования sync и await.

Sign up to leave a comment.