Pull to refresh

Comments 46

Меня насторожило использование promise-ext-settled — он добавляет своё свойство «allSettled» в нативный Promise конструктор. Так и до Prototype.js недалеко.
Array.prototype.concat.apply([], Array.prototype.slice.call(arguments));

Автору не плохо бы самому на собеседование сходить. За подобное использование arguments стоит бить по рукам, тк из-за этого функция никогда не будет оптимизирована в v8.
Для этого в ES6 придумали spread оператор:
function foo(...args) {
  console.log(args);
}
Видимо у Вас перманентное желание бить всех по рукам :)
Если Вы присмотритесь, то увидите, что я не использую ES6(2015) синтаксис.
И не использую я его сознательно, чтобы не привлекать библиоетеки типа babel и не увеличивать итоговый размер скрипта
Не совсем понятны Ваши претензии. Если считаете, что Вам есть что добавить к коду — милости прошу pull request. На то он и github. Это всяко лучше и продуктивнее саркастически высказываний ;)
Да и не безымянная это функция. Это Function Expression. Если внутри нее возникнет исключение, то в stack trace будет имя функции, в которой возникло исключение.
Это именно что безымянная (анонимная) функция созданная функциональным выражением и присвоенная переменной (MDN). В stack trace у вас будет имя переменной а не имя функции (Function.name).
У вас-же, в комментарии, всё смешанно в одну кучу, что неправильно.
Es6 умеет определять имя функции в var someName = function expression.
Es5 в данном случае будет содержать имя переменной, которой была присвоена функция
Последний хром:
var foo = function() {}
undefined
foo.name
""
Если трансформировать этот код через babel, который поддерживает практически все последние нововведения в язык, то всё будет работать именно так, как я описал ;)
Собственно, проблема то в чём?
скриншот
image

Докопались до какой-то ерунды. Даа foo.name будет пустым, но в чём трагедия то? Может ещё от => откажемся? Отладка неименованных анонимок уже давно перестала быть чем-то невыносимым.
Ради экономии сотни байт вы теряете в читаемости и производительности? Странная экономия на спичках.
В прочем вполне можно обойтись и без babel, самостоятельно приведя аргументы к массиву.
function foo() {
  var args = new Array(arguments.length);
  for (var i = 0; i < arguments.length; i++) {
    args[i] = arguments[i];
  }

  console.log(args);
}
Да само-собой, можно как угодно, но притензия у комментатора была в том, что я не использую spread(которого нет в es5)
Нет, претензия была в том, что не надо писать код, который не оптимизируется и плохо читается. Надо писать не как угодно, а обдуманно.
Вопрос читаемочти кода из разряда холивара :)
За исключением явного нагромождения кода.
Зная, что concat может принимать как массив, так и обычные значения, превращая их а плоский массив — такая, запись становится очень даже понятной
Да само-собой, можно как угодно, но притензия у комментатора была в том, что я не использую spread. Но я не использую здесь es6 ;)
Вы пишете «странная экономия на спичках», а затем постите 4 строчную конвертацию arguments в array. Где логика? К тому же Array.prototype.slice.call это уже давно классика.
теряете … в производительности
Даёшь asm.js в каждый метод? :)

Устроили целый тред из-за какой-то субъективной глупости.
Давайте ещё найдём у автора for(…; …; i ++); и устром тред про то, что ++ i будет быстрее ;)
Нет. Я автору предложил использовать ES6 по полной, автор отказался, я показал альтернативный метод.
К тому же Array.prototype.slice.call это уже давно классика.

Продолжайте себя в этом уверять и не изучать изменения в языке.
Продолжайте себя в этом уверять и не изучать изменения в языке.
Я использую и ES6 и ES7. А до ES6 использовал node-fibers. Не делайте скоропостижных выводов. И не беритесь учить окружающих, не понимая сути. Ваша экономия на arguments по большей части неуместна. Воевать вот с этим имеет смысл только в высоко-нагруженных местах.
И да, забыл упомянуть: смысл именно такого concat'а в том, чтобы можно было передавать обычные значения и значения в миссиве в перемешку межлу собой и в последствии получить плоский массив
С удовольствием прочитаю cool story, где [].slice.call(arguments, 0) стал узким местом, да и не одним v8 едины.
В коде подобных библиотек — вполне может стать. Дело даже не в том, что приведенный мной вариант быстрее (в 4-30 раз), а в том, что вся функция не будет оптимизирована. Если эта библиотека используется на сотнях, или тысячах картинок это может вылиться в видимую задержку.
да и не одним v8 едины

Абсолютно верно, остальные движки еще медленнее в этом месте: jsperf.com/arguments-spread
И? Я как раз взял за основу ваш тест #2 и добавил хоть какое-то «полезное» действие.
О, Зевс и что? А если завтра что-то измениться, и светлая голова для операции slice.call(arguments) добавит оптимизацию, как они сделали с fn.apply, что тогда? А вы мне микробенчмарки суете, которые r реальному миру имеют довольно посредственное отношение. Ну и главное, что я хотел, писать «За подобное использование arguments стоит бить по рукам» — попахивает снобизмом.
Да почему снобизм-то? Если постоянно забивать на подобные мелкие недочеты, весь проект в целом будет работать медленнее, чем мог бы.
Да потому что не будет, в реально проекте, тормозит всё что угодно, но не подобные вещи. Есть просто разные уровни, в библиотеках вроде lodash, такие оптимизации оправданы. НО! Сам lodash существует только потому, что нативные реализации не оптимизированы, хотя ситуация меняется.

И ещё, в идеально мире, Array.from должен был показывать максимальный результат, но мы опять имеем какую-то лажу. Остается только надеяться на ...rest, но…

Прямо сейчас, V8 не оптимизирует функции, где есть try/catch/finally, let, const, for-of и что то там еще.
За это тоже по рукам бить?

P.S. Я знаю, что сейчас про jQuery уже все забыли, но когда компы были слабые, а я переходил с IE6 на Mozilla, никого не смущало, что там уйма таким мест, как в прочем и сейчас. Собственно и код таких популярных решений как React (до 0.12.0, дальше они на ES6 перешли), Angular и других, только подтверждают мой посыл, по рукам никого не бьют.
Ох, ребята… Да о чем вы все вообще? Это интерпретируемый язык, что изначально говорит о том, что каждый движок интерпретирует его по своему. А если учесть, что в любой последующей версии любого движка могут прикрутить любую оптимизацию, которая кстати может убить ту оптимизацию, которую проделал разработчик, то вся эта оптимизация со стороны разработчика становится погоней за миражом.
Пишите код, получайте от этого удовольствие и не забывайте о производительност и эффективности своих алгоритмов(большое «О»). Но заниматься оптимизацией под лупой в интерпретируемом языке — неблагодарное дело, по причине, которую я описалвыше, кроме совсем явных мест.
Другое дело, если вы точно знаете под какой движок пишите и излазили его исходники вдоль и поперек.
Так же согласен с предыдущим оратором. Посмотрите на код современных не ES6 библиотек. Где там читаемость? Там сплошная оптимизация.
К самой статье-то есть комменты? :)
Потому что каждой оптимизации своё место. Обращать внимание на arguments и пр. примеры из проблем V8 имеет смысл, если вы ожидаете, что этот метод будет вызываться десятки тысяч раз. К примеру вы пишете игру, и ваша задача повысить FPS. Там и asm.js не грех использовать.

Весь остальной же код должен быть в первую очередь хорошо читаемым, легко расширяемым, и т.д.
Проблема любых видов экономии на спичках заключается в том, что это самая крайняя мера. Перед тем как заняться ею, необходимо исправить другие проблемы, которые дадут вам выигрыш на 1-2 порядка по производительности. Например в особо важных местах отказаться от любой реактивщины. Оптимизировать работу с DOM. Параллельные запросы, вместо последовательных. Ленивые вычисления. Ну и т.д.

Тут ребята изолируя тело try-catch конструкции в отдельный метод до 1600% прироста производительности получают. Но это не повод так делать, в нормальном коде, который не требует запредельной производительности. Никто не заметит, что ваш метод выполнился за 0.001ms вместо 0.0001ms.
Для этого в ES6 придумали spread оператор
Spread оператор хорош, но это ES6. И, кстати, далеко не факт, что такой код в ближайшее время будет оптимизирован. Не так давно мелькала статья, с причинами не-оптимизации. И по большому счёту, чтобы туда не угодить, нужно свой код под лупой разглядывать. Для некритичных к производительности участкам кода ― в этом смысла мало.

Не вижу никакого криминала в старом добром
Array.prototype.slice.call(arguments)

Вот правда, что-то я замешкался, относительно ещё и concat-а в этом месте… smelukov, а зачем он тут?
Возможно Вы в первый раз сталкиваетесь с расширением встроенных конструкторов?
Хорошо бы с этим более никогда не сталкиваться. PrototypeJS я думаю хватило всем выше крыши.

Правильным будет считать только подходы:
1. с расширением symbol-ми (и то под большим вопросом)
2. с наследованием от стандартных объектов
3. внутре-модульной реализацией всего что нужно

А для прямого засорения штатных прототипов не осталось ровным счётом ни одного аргумента.
Или, возможно, Вы о том, что можно было не расширять конструктор, а просто вернуть функцию allSettled из модуля?
Как вариант. Но здесь на вкус и цвет… Я реализовал так.
Во первых, большое спасибо за статью, тема промисов действительно интересна и полезна.

На счёт расширения конструктора, я постараюсь аргументированно ответить.
Я считаю, что это отнюдь не на вкус и цвет. Сам принцип модульности заключается в том, что то, что происходит внутри модуля — остаётся внутри модуля.

Допустим я подключил ваш Image preloader и использую его.
Допустим, я подключил так же другую утилитарную либу N, которая тоже вдруг решила, что нативным Промисам страсть как не хватает так сказать статического свойства allSettled. Я получу конфликт имён и грязно ругаю обе библиотеки, которые позволяют себе такую наглость.

Патчинг свойств глобального Promise нисколько не лучше глобальных переменных и несёт всё те же проблемы и подводные камни.

Да, я бы хотел получить функцию allSettled как экспорт из модуля, раз уж мы работаем в модульной среде. Если мне страсть как понадобится положить его в конструктор Promise, я это смогу сделать в любой момент.
Promise.allSettled = require('promise-ext-settled');


Ваш способ не оставляет выбора.
Да, возможно стоит обдумать этот момент и не выносить allSettled в конструктор, т.к. в данном случае это не полифил, которым расширяется прототип
Хотя, в случае с полифилами, которые расширяют встроенный функционал, все равно придется расширять прототип.
Это как раз приемлемый вариант. Главное чтобы спецификация была уже более менее устаканена.
Счел Ваши мысли вполне логичными и, так как allSettled не является полифиллом и не расширяет прототип, то сделал из него полноценный модуль без внесения изменений в нативные конструкторы ;)
Можно еще вот так делать. Что, кстати, и рекомендует делать автор bluebird, из-за преемственности со стандартным try-catch потоком.
.then(function() { 
  throw "Error";
})
.catch(function() {
  return "Fallback";
})
.then(function(result) { 
  console.log(result);
  // Получаем fallback
});

Не понял логику. Зачем ошибку возвращать в then?

Получается, что, если асинхронная операция будет выполнена успешно, то выполнится onFulfilled callback в первом then, а далее onFulfilled callback во втором then.
Но что, если асинхронная операция завершится неудачей? Выполнится onRejected callback в первом then, а затем(внимание!) onFulfilled callback во втором then.
Почему? Смотрите выше правило для then-callback.
Исходя из него — чтобы вызвать следующий onRejected callback(которого кстати нет), необходимо: либо вернуть промис, который будет rejected, либо выбросить исключение.

А вот тут огромная СПАСИБА! :)


Я раньше оборачивал в новый Promise, теперь буду знать.

Sign up to leave a comment.

Articles