Pull to refresh

Comments 9

Во-первых, не понятно что делать если [...] выбросят несколько ошибок
Ну тут, очевидно, два варианта. Либо ловить первую, либо собирать их в массив. Promise.all работает по первой стратегии: если один из входов фейлит, то фейлит и их композиция с этой ошибкой. Тут плюс в том, что не нужно дожидаться ответов от всех входов, можно продолжать после первого фейла. И с другой стороны, попробуйте придумать ситуацию, когда нам действительно нужно получить весь массив ошибок? Как правило, нам достаточно того, что ошибка совершилась, и нужно переходить к её обработке. А что там с остальным, так разберёмся, когда этот блок перестанет выдавать ошибку.

А если мы уже ушли далеко вниз а в каком-нибудь parEach выше по таймеру выскочила ошибка?
Я, наверно, не до конца понимаю идею этой библиотеки. Как мы можем «уйти вниз», если ещё не получены все результаты? Документация грит:
parEach waits for all actions to call this() before moving along to the next action in the chain.
Если она ждёт ответа ото всех входов, то она дождётся и любой ошибки.

Поэтому, я решил, что в каждом YAFF должна быть только одна конструкция для обработки ошибок и она должна быть в конце.
Так нельзя, это всё равно что предлагать в синхронном программировании сделать один блок try-catch на всю программу и обязать программистов размещать catch в main. Многоуровневые catch просто необходимы. Например, у нас есть асинхронная либа, которая кормит нас такими потоками (flow). У неё есть внутренние ошибки, например, вызванные отсутствием файлов. Но на выходе, она преобразует невнятные внутренние ошибки в понятные ошибки из своего API. Или ещё пример: либа скармливает нам типизированные ошибки, мы хотим отлавливать их по-отдельности, на разных уровнях. Для всего этого нужны многоуровневые catch.

Смысль финального catch, это отловить все ошибки, чтобы программа хотя бы не падала, но в действительности, может быть ещё много промежуточных catch под более конкретные ситуации.
Есть еще forEach и он не ждет ничего.
Со временем я пришел к тому, что в моем коде остался только один catch и после seq для обработки удачного результата. Это примерно как в эрланге let it crash — мы программируем только оптимистичный случай когда все работает как надо. Если что-то пошло не так — ловим ошибку в finally и возвращаем тому кто вызывал: другая функция поймает ошибку точно так что в свой finally и таким образом она всплывет к вызывающему.
Catch вызывает много проблем с пониманием того куда передается поток управления. Вы использовали оригинальную библиотеку (Seq)?
мы программируем только оптимистичный случай когда все работает как надо
Да, понял стратегию. Это достаточно просто и наглядно, и ещё я думаю более производительно в реализации.

Оригинальную библиотеку я не использовал, потому у меня и возник такой вопрос. Я много использую Promise, но от основной идеи далеко не уйдёшь, асинхронный поток он и есть поток. В вашем примере указан как раз perEach, как я понял из документации, он-таки дожидается результата. forEach — нет, но в документации не сказано, что forEach сам может быть эмитентом ошибки. Вероятно, forEach нужен для вызова сайд-эффектов, поэтому он не влияет на состояние потока. В других либах для этого есть функция tap или doto.
К слову, это библиотека не является заменой обещаниям. Я использую и то и другое. Свою библиотеку — когда нужно организовать такой вот асинхронный поток. Обещания — когда этот поток может вызываться из разных мест или результаты работы нужны в разных местах, ну и для кеширования(или лучше сказать мемоизации?). Они прекрасно друг друга дополняют.
Рекомендую обратить внимание на Highland. Он несовместим с node-seq, как ваша библиотека, но в целом очень интересен, например, умеет принимать всё, от массивов до функций, промисов и потоков ноды. Просто ознакомьтесь с некоторыми решениями, очень интересная либа.

А по поводу отлова ошибок я бы хотел развеять ваше предубеждение, что их поведение неочевидно. Как правило, по взгляду на код можно определить как срабатывают catch, есть простое правило:
1. catch отлавливает все ошибки, возникшие выше него.
2. Если отработавший catch не выбросил ошибку сам, то она считается отловленной и следующие обработчики ошибок не срабатывают.
3. Если же он породил новую ошибку, то она будет передана ниже.
Так это реализовано в Promise и это очень наглядно (и тут полная аналогия с синхронным try-catch, только без многих уровней вложенности). Я думаю в node-seq такой же механизм, поэтому задавал вам уточняющий вопрос.
А с catch в оригинальной библиотеке происходили чудеса. В каких то случаях, после того как туда ловилась одна ошибка из того же parEach, библиотека переходила к тому, что написано после catch. И у меня даже были какие то костыли для обхода этой ситуации. Просто в какой то момент надоело и решил сначала исправить, а потом и переписать. Вот, смотрите:
  this.Seq([1,2,3,4,5,6,7])
    .parEach(function (num) {
      if (num % 2 == 0)
        return setTimeout(function () {this('ha-ha!')}.bind(this), 100)
      this(null, num);
    })
    .catch(function () {
      log('orig catch:', arguments);
    })
    .seq(function () {
      log('regual seq', arguments);
      this();
    })
    .catch(function () {
      log('should not be called', arguments)
    });

Этот код (использует оригинальную библиотеку) даст такой результат:
orig catch: { '0': 'ha-ha!', '1': 1 }
regual seq { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6, '6': 7 }
orig catch: { '0': 'ha-ha!', '1': 3 }
regual seq {}
orig catch: { '0': 'ha-ha!', '1': 5 }
regual seq {}

т.е. элемент после catch будет вызван несколько раз. Перый раз — как положено, с аргументами. А потом — непонтно как.
UFO just landed and posted this here
В общем то, получается примерно то же, что придумал substack и переписал я.
iojs, конечно, хорошо. Но существующие проекты и на 0,12 ноду перевести не всегда можно и насчет будущего iojs у меня пока сомнения; поживем — посмотрим как они будут развиваться и куда двигаться. (Ужасно не люблю когда вот так вот плодят сущности.)
UFO just landed and posted this here
Sign up to leave a comment.

Articles