Pull to refresh

Comments 18

Вот поэтому разработчики, если мне не изменяет память, Axios, под капотом используют вместо fetch старый добрый XMLHttpRequest

А ещё с fetch нельзя реализовать индикатор загрузки файла на сервер (upload progress). Поэтому убийцей XMLHttpRequest его никак не назвать.

Можно уточнить, при отмене Promise что должно происходить с ожидающим его onRejected? Раз мы говорим об интерфейсном соглашении, надо договориться о какой-то осмысленной ошибке.
  1. В вашем коде куча ошибок. Я, конечно, понимаю, что вы достиги дзена и можете интерпретировать код в голове. Но браузеры ещё не достигли вашего совершенства, чтобы выходить из бесконечного цикла за конечное время.


  2. Я тут переписал ваш код на файберах с отменяемыми запросами на любой стадии исполнения:

Заголовок спойлера
const log = document.getElementById( 'log' )

let fetching = null

const fetchInformation = ()=> {

  fetching = $mol_fiber_make( ()=> {
    log.innerText = 'Fetching...'
    const text = fetch({ uri : 'some.csv' }).responseText
    log.innerText = JSON.stringify( parseCSV( text ) )
  } )

  fetching.start()
}

const cancelFetch = ()=> {
  fetching.destructor()
  fetching = null
  log.innerText = 'Cancelled'
}

const fetch = ({ uri })=> {

  return $mol_fiber_async( back => {

    const xhr = new XMLHttpRequest()

    xhr.open( 'GET', uri ) // API endpoint URL with some big CSV database

    xhr.onload = back( () => {

      if( Math.floor( xhr.status / 100 ) !== 2 ) {
          throw new Error( xhr.statusText )
      }

      return xhr
    } )

    xhr.onerror = back( () => {
      throw new Error( xhr.statusText )
    } )

    xhr.send()

    return ()=> xhr.abort()

  } )

}

const parseCSV = $mol_fiber_func( text => {

  let lastDemileterIdx = 0
  let result = []

  do {

    const newIdx = $mol_fiber_sync( ()=> text.indexOf( '\n' , lastDemileterIdx ) )

    const row = $mol_fiber_sync( ()=> {
        log.innerText += '.'
        const end = newIdx > -1 ? newIdx : Infinity
        return parseCSVRow( text.substring( lastDemileterIdx , end ) )
    } )

    result.push( row )
    lastDemileterIdx = newIdx + 1

  }  while( lastDemileterIdx > 0 ) 

  return result
} )

/* Some function for row parsing which works very slow  */
const parseCSVRow = text => {

  for( const from = Date.now() ; Date.now() < from + 5 ; ) {}

  return text.split( ',' )
}

http://plnkr.co/edit/vPKpKKwIhALm1lUEIrKu?p=preview

Решил разобраться в вашей fibers магии и обнаружил, что $mol_fiber_make обязана быть чистой функцией:


let requestCount = 0;

const fetchInformation = () => {
  fetching = $mol_fiber_make(() => {
    requestCount++;
    log.innerText = requestCount;
    const text = fetch({ uri : 'some.csv' }).responseText;
  });
  fetching.start();
}

Такой код насчитает два запроса вместо одного.


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

Нет, функия должна быть идемпотентной, но не обязательно чистой. Неидемпотентные вызовы просто заворачиваются в $mol_fiber_sync и они становятся идемпотентными.
Дока с тестами тут: https://github.com/eigenmethod/mol/tree/master/fiber
Вообще весь $mol построен на той же концепции, так что в продакшене вполне нормально с нею живётся.


А ваш код должен выглядеть так:


let requestCount = 0;

const fetchInformation = () => {
  fetching = $mol_fiber_make(() => {
    $mol_fiber_sync( ()=> requestCount++ );
    log.innerText = requestCount;
    const text = fetch({ uri : 'some.csv' }).responseText;
  });
  fetching.start();
}

То, что есть решение — это хорошо, но не стоит забывать зачем все эти fiber-подобные решения появились — чтобы сделать работу с асинхронностью более удобной и компактной. Заворачивание в $mol_fiber_sync в этом не помогает, я уж лучше на промисах как-нибудь, от них хотя бы понятно чего ждать

Ситуаций, где необходим неидемпотентный код крайне мало. Соответственно, и $mol_fiber_sync вставлять приходится крайне редко. И то в основном для оборачивания тяжёлых функций, чтобы они не блокировали поток на долго.

В bluebird есть возможность отмены уже много-много лет. Как и другие вещи, которых нет в нативных промисах. Как и скорость, превосходящая нативные промисы...

Как и скорость, превосходящая нативные промисы...

В этот момент всегда нужно приводить пруфлинк. Результаты в репозитории проекта устарели года на 3. Я сейчас на коленке собрал свой бенчмарк, и у меня нативные Promise выиграли (браузер Chrome, MacOS).


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

Дата обновления бенчмарка — март 2017.
Дата выхода используемой версии node.js — март 2017. Далеко не три года, как вы говорите — меньше года.


Или вот вам ещё более свежие бенчмарки прямо от ребят из nodejs. На turbofan всё равно быстрее оказывается bluebird, и их это огорчило.


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


Ну и так, чисто поржать — например, Promise.finally есть только в ES2018. А в bluebird я им уже много лет пользуюсь.

Или вот вам ещё более свежие бенчмарки прямо от ребят из nodejs.

Спасибо за интересную ссылку, много полезной информации. Несколько важных пунктов оттуда:


  1. Bluebird заточен чтобы побеждать в своем конкретном бенчмарке. Совпадает ли это с реальными use-case — неясно.
  2. Тем не менее разрыв между Bluebird и нативными промисами сокращается с каждой новой версией V8.
  3. Большинство пользователей сейчас пользуются async/await функциями, которые могут быть сильнее оптимизированы, чем цепочки .then(). В идеале нас будут ждать async/await функции с оверхедом как у синхронных.

Вывод все такой же: Bluebird — прекрасный проект, образцовый пример для разработчиков нативных промисов, но считать его быстрейшим на все времена — не стоит.


Ну и так, чисто поржать — например, Promise.finally есть только в ES2018. А в bluebird я им уже много лет пользуюсь.

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


Добавить фичу в основу языка намного сложнее, нужно поддерживать обратную совместимость. Поэтому ничего смешного в том, что нативные промисы развиваются медленее, нет. Они учитывают опыт user-land библиотек, в том числе и Bluebird.

Bluebird заточен чтобы побеждать в своем конкретном бенчмарке. Совпадает ли это с реальными use-case — неясно.

Видимо, вы не очень внимательно читали ссылку — там приводилась и сразу же опровергалась эта гипотеза, и был сделан вывод, что блюбёрд просто сделан так, чтобы побеждать любые бенчмарки.


Тем не менее разрыв между Bluebird и нативными промисами сокращается с каждой новой версией V8.

Да, поэтому когда-нибудь в 2020 году, когда нативные таки обгонят bluebird по производительности и добавят всё нужное, я спокойно удалю его из своих проектов. Хотя, может это будет и 2030… До тех пор не вижу в этом смысла.


Добавить функцию в библиотеку намного проще, чем в стандарт.

Естественно проще. Но для этого и есть функции, помечаемые как экспериментальные. Более того, в стандарте ещё и breaking changes случаются так же, как и в библиотеках — навскидку могу сказать, что очень позабавило изменение crypto.md5 с аргументами по умолчанию. Это, правда, пример из node.js а не ES, что не совсем корректно, но тем не менее.


Поэтому ничего смешного в том, что нативные промисы развиваются медленее, нет.

Смешно то, сколько лет потребовалось для введения промисов — и то они получились куцые и медленные. Несмотря на "учтение всего опыта". Ну и особенно смешно, что постоянно вижу агрессивную защиту и одновременно допиливание нативных промисов с одним единственным аргументом: "ну они же нативные!"

Я тоже собирал свой бенч месяц назад. Прирост производительности Bluebird в тесте показал десятикратный. В боевой нагрузке тоже поехало быстрее (не в 10 раз, конечно, но заметно). Доберусь до домашнего компьютера — поделюсь.
Не кажется ли это действие попыткой угнаться за RxJs?

Спасибо за статью! В своё время пытылся решить проблему с недостатком отмены асинхронных действий в Promise. Разработал решение именуемое Deal — потомок Promise с встроенным медотом cancel и eventEmitter для взаимодействия с асинхронным процессом через Promise, которое находится в составе библиотеки Minimalist (https://github.com/mr-amirka/minimalist). Здесь метод cancel может отменять всю цепь и агрегацию из Promise. К сожалению, пока не хватает времени дописать всю документацию. Если кому-то действительно нужен отменяемый Promise, то рекоммендую обратить внимание.

Only those users with full accounts are able to leave comments. Log in, please.

Information

Founded
2011
Location
Россия
Website
netology.ru
Employees
201–500 employees
Registered