Почему родные обещания ES6 медленнее и требуют больше памяти, чем bluebird?

195

В этом тесте комплекту обещаний ES6 требуется в 4 раза больше времени, чем обещаниям Bluebird, и он использует в 3,6 раза больше памяти.

Как библиотека JavaScript может быть намного быстрее и легче, чем собственная реализация v8, написанная на C? У обещаний Bluebird точно такой же API, как у обещаний ES6 (плюс куча дополнительных служебных методов).

Является ли нативная реализация просто плохо написанной, или есть какой-то другой аспект, который я упускаю?

Каллум
источник
Имейте в виду, что современные реализации JavaScript сильно оптимизированы и могут даже работать с использованием JIT .
1
Согласно This Benchmark , BlueBirdJS на самом деле медленнее, чем Native Promises. Но PromiseMeSpeedJS на самом деле опережает их обоих. Одна из многих вещей, которые PromiseMeSpeedJS доказывает благодаря этому, заключается в том, что основным виновником выполнения обещаний является злоупотребление newоператором, потому что PromiseMeSpeedJS не использует new.
Джек Гиффин
1
@JackGiffin Chrome 67: PromiseMeSpeedJS медленнее на 46%, а Bluebird медленнее на 61%.
FINDarkside

Ответы:

272

Автор Bluebird здесь.

Реализация обещаний V8 написана на JavaScript, а не на C. Весь JavaScript (включая собственный V8) скомпилирован в нативный код. Кроме того, написанный пользователем JavaScript оптимизируется, если это возможно (и того стоит), перед компиляцией в собственный код. Реализация обещаний - это то, что не принесет особой пользы от написания на C, а на самом деле только замедлит ее, потому что все, что вы делаете - это манипулируете объектами JavaScript и связью.

Реализация V8 просто не так оптимизирована, как bluebird, она для экземпляров выделяет массивы для обработчиков обещаний . Это занимает много памяти, когда каждому обещанию также приходится выделять пару массивов (эталонный тест создает в целом 80 000 обещаний, так что выделяется 160 000 неиспользуемых массивов). В действительности, 99,99% вариантов использования никогда не выполняют обещание более одного раза, поэтому оптимизация для этого общего случая дает значительные улучшения в использовании памяти.

Даже если V8 реализует те же оптимизации, что и bluebird, это все равно будет затруднено спецификацией. Тест должен использовать new Promise(анти-паттерн в bluebird), так как в ES6 нет другого способа создать рутинное обещание. new Promiseэто чрезвычайно медленный способ создания обещания, во-первых, функция executor выделяет замыкание, во-вторых, в качестве аргументов передается 2 отдельных замыкания. На каждое обещание выделяется 3 замыкания, но замыкание уже является более дорогим объектом, чем оптимизированное обещание.

Можно использовать Bluebird, promisifyкоторая позволяет выполнять множество оптимизаций и является гораздо более удобным способом использования API-интерфейсов обратного вызова, а также позволяет преобразовывать целые модули в модули на основе обещаний в одну строку ( promisifyAll(require('redis'));).

Esailija
источник
10
«по-прежнему препятствовать спецификации» - не уверен, что это значит. Вы говорите, что ES6 следует спецификации, которая по своей сути медленная, и если это так, значит ли это, что bluebird не следует той же спецификации (и если это так, то следует ли она другой и какой)? И есть ли какая-то причина, по которой у ES6 не может быть лучшего способа создания корневого Promise, кроме как new Promiseили улучшения его создания, чтобы сделать его менее дорогим (например, не создавать 3 замыкания на экземпляр)?
Энтони
12
Это не звучит хорошо (для JS). Я действительно не хочу использовать библиотеку Promise, когда есть внутренняя реализация. Это более чем прискорбная ситуация для всех, если все это правда. Но у меня уже есть проблемы с просмотром Promise-ажиотажа, я написал 100 000 приложений LoC JS и до сих пор не вижу в этом реальной необходимости, это очень незначительное улучшение, если вообще для меня , в основном в обработке ошибок, нет улучшение обработки обратных вызовов (я никогда не был в «аду обратных вызовов» со своим стилем кодирования).
Мёрре
19
В ES6, вы не можете использовать Promise.resolve()для создания "root обещание"?
Цетлен
10
@ MörreNoseshine (продолжение) Несколько лет спустя авторы ES6 пришли и сказали: «Эй, давайте уточним, что движки JS должны предоставлять стандартную утилиту, соответствующую Promises / A +, из коробки, поэтому у людей всегда есть основной инструмент обещания под рукой». ». Это приятное удобство (не нужно импортировать библиотеку просто для быстрого Promise.resolve()или чего-то еще), но это очень простая реализация, и ее существование не должно отталкивать вас от использования более серьезных инструментов, связанных с обещаниями, таких как bluebird!
Каллум
11
@ MörreNoseshine 100k LOC Javascript приложение, которое, вероятно, никогда не имело никакой асинхронной функциональности. Удачи в написании 100K LoC JS-игры с библиотекой mysql / redis без bluebird.
NiCk Newman