Комментарии 137
Он использует короткое замыкание во избежание лишних затрат
Перевести «short-circuiting» как «короткое замыкание» в контексте программирования, когда уже введен термин «closure» (который также переводится как замыкание) может только непрограммист.
Согласно MDN оператор "+" действительно является самым быстрым способом преобразования строки в число
Нет, не самый быстрый: jsbench.me/6ok6dbgbeo/1
Прототип несуществующего свойства объекта имеет undefined в качестве значения по умолчанию
У несуществующего свойства нет прототипа.
true || b — сразу true, b не вычисляется, так как незачем.
false && b — сразу false, b не вычисляется, так как снова незачем.
Ну т.е. это просто стоило бы пояснить. Особенно если не знаешь, как это в русском называется.
>только непрограммист.
100%.
Выполнение короткого замыкания
Так как логические операторы выполняются слева направо, они тестируются на возможные «короткие замыкание», при использовании следующих прав:
false && (anything) короткое замыкание, дающее false.
true || (anything) короткое замыкание, дающее true.
2. Цитата с MDN: унарный плюс — быстрейший и предпочитаемый способ конвертирования чего-либо в число потому, что он не выполняет каких-либо операций с числом.
3. Из статьи: Remember properties in an object which does not exist in itself and its prototype has a default value of undefined and undefined has no property x. Согласен, что не вполне корректно. Вероятно, автор имел ввиду, что obj.__proto__.__proto__ = null, поэтому возвращается undefined, у которого, естественно, отсутствует свойство x.
Видимо раздел логические операторы на MDN переводил тоже непрограммист.
Ну, видимо… Там, в MDN, они хотя бы взяли идиому «короткое замыкание» в кавычки и разжевали что это значит. Ваша фраза: «короткое замыкание во избежание лишних затрат» ничего не объясняет, а только запутывает. Я бы вообще назвал это «ленивые вычисления», хотя это также неточно и ambiguous.быстрейший и предпочитаемый способ конвертирования чего-либо в число потому, что он не выполняет каких-либо операций с числом
А какой способ, по-вашему, выполняет операции с числом?Number('123')
выполняет? АparseFloat('123')
? Чем они хуже? Чем они медленней?obj.__proto__.__proto__ = null
Автор имеет ввиду, что свойстваsomeprop
нету ни у объекта, ни у его прототипа, по-этому оно undefined. Аobj.__proto__.__proto__ === null
будет верно для любого простого объекта. Это значит только, что вы находитесь в конце цепочки прототипов. Но это не значит, чтоobj.__proto__.someprop === undefined
взяли идиому «короткое замыкание»
это не идиома, а дословный перевод, бессмысленный и беспощадный.
в моей стране обычно переводят как «вычисление по короткой схеме».
А какой способ, по-вашему, выполняет операции с числом?Предположу, имелось в виду то, что если на вход подать уже готовое число, а не строку, то тогда будет быстрее.
И это не лишено смысла, потому что по моим наблюдениям никто и не использует плюс, если действительно по бизнес-логике нужно парсить строку — там используют parseInt/parseFloat. Плюс же используют для перестраховки, например, аргументов внутри функции — типа, мне тут и так должно приходить число, но на всякий случай фигану-ка я сюда ещё плюсик!
быстрейший и предпочитаемый способ конвертирования чего-либо в число
а автор пишет:
является самым быстрым способом преобразования строки в число
т.к.
parseFloat(true) = NaN
+true = 1
И видимо, об этих дополнительных операциях шла речь. Только автор ничего не понял. И написал всё это про число-в-строку, обогатив мир ещё одним авторитетным домыслом.
PS: конвертация всего-что-угодно в строку — верный признак плохого кода.
На iPhone SE плюсик действительно самый быстрый оказался.
Если профессиональное сообщество реагирует отрицательно, значит, статья получилась плохой.
Или сообщество недостаточно профессионально и (или) компетентно. Оглядываться на мнение толпы, особенно в таких сомнительных местах, как хабр — очень плохой паттерн как для саморазвития в целом, так и самооценки в частности.
console.log('' && true && 5)
в комментарии ответом указана 5, хотя даже новички знают, что приведение пустой строки к логическому значению в Javascript вернёт false.
Не правильный ответ.
Они будут принципиально отличаться.
Вообще, в зависимости от того, как мне отвечают на этот вопрос я понимаю знает ли человек принципы работы JavaScript или сразу с React начал.
Он гарантированно запускает код каждые n миллисекунд. Даже если то, что он запускает ещё не успело выполниться. Ему без разницы.
Цикл из setTimeout же, в силу реализации, сначала будет дожидаться выполнения кода (хотя можно конечно же сделать чтобы не дожидался), а затем запускать новую копию кода.
Вы делаете слайдер для своего сайта и пишите анимацию слайдов с использованием setInterval. Неверно. Сверните окно браузера, разверните его через 5 минут и посмотрите что получится.
Верно: анимировать слайды с помощью зацикленного setTimeout.
Скажем, у нас есть код, который выполняется 400 мс. И есть период каждые 1000 мс.
В случае с setInterval вторая копия кода будет запущена через 1000 мс, а в случае с зацикленным setTimeout через 1400 мс.
И тут можно задать бонусный вопрос: как сделать так, чтобы зацикленный setTimeout был идентичен по повадкам вызову setInterval.
codepen.io/barkalov/pen/mdJypjB?editors=0010
codepen.io/barkalov/pen/xxGbpaV?editors=0010
Простите конечно, но вы бред городите.
Во-первых, начнем с того, что ни setTimeout, ни setInterval не запускают код. Они лишь регистрируют коллбек. Запускает код уже внутреннее API, которое напрямую связано с такой штукой, как eventloop. Это и есть сердце асинхронности и работы всех асинк-функций. Так вот, что в случае с setTimeout, что в случае с setInterval, код из следующего коллбека будет запущен только и только тогда, когда в callstack'e не будет никаких операций. Поэтому setInterval ничего не запускает и уж тем более ему не все равно, он просто регает коллбек. Почему анимация дергается когда она на setInetrval — так это потому, что регистраций колбеков setInterval делает больше (в тот момент, когда callstack пустует), чем setTimeout. setTimeout более контролируемый, скажем так. А вообще анимации делать неправильно ни через то, ни через другое. Для анимаций есть давно requestAnimationFrame.
Они будут принципиально отличаться.
И вы, конечно, назовёте эту принципиальную разницу?
Он гарантированно запускает код каждые n миллисекунд.
setInterval() не только ничего не «запускает» гарантированно, но даже и в стек выполнения не кладёт гарантированно. И то и другое — произойдет по обстоятельствам. Гарантируется здесь лишь то, что от прошлого заталкивания в стек пройдет минимум столько миллисекунд, сколько указано в задержке.
Цикл из setTimeout же, в силу реализации, сначала будет дожидаться выполнения кода (хотя можно конечно же сделать чтобы не дожидался), а затем запускать новую копию кода.
Если вы немедленно после срабатывания прошлого setTimeout() вызовете следующий — то никакого «дожидаться выполнения кода» там, разумеется, не будет.
Еще раз, где тут принципиальная разница, которую вы залихватски наобещали?
Вы делаете слайдер для своего сайта и пишите анимацию слайдов с использованием setInterval.
Того, кто в 2020 году пишет JS-анимацию НЕ через requestAnimationFrame() — следует увольнять за профнепригодность вне зависимости от того, на сколько глупых вопросов по JS он может ответить на собеседовании.
const timeout = function(fun) {
console.time('timeoutFun');
setTimeout(fun, 1000);
}
const dowork = async function() {
// "do lots of work"
console.time('work');
let s = '';
for (let i = 0; i < 1000000; i++) {
s += Math.random() > 0.5 ? '+' : '-';
}
console.timeEnd('work');
}
const run = function() {
const fun = (counter) => async () => {
console.timeEnd('timeoutFun');
if (counter++ < 10) {
timeout(fun(counter));
}
dowork();
}
timeout(fun(0));
}
run()
Оно выполняется точно так же, как подобный код выполнялся бы с setInterval(). Пока dowork() не больше задержки таймера, он будет выполняться в промежутках между перезапусками setTimeout(). Когда станет больше — будет тормозить и сами перезапуски. То же самое произойдет и с setInterval() в такой ситуации.
Вам по существу-то есть что сказать? Я всё еще надеюсь услышать от вас про принципиальную разницу между setInterval() и setTimeout().
реализация через setTimeout не допустит одновременного запуска двух копий кода
Одновременного запуска кода в JS вы в любом случае не получите, как бы вы не пытались.
Вы ведь прекрасно поняли о чём я говорю.
Если браузер занят выполнением кода, новый вызов в стек выполнения упадёт тогда, когда браузер освободится — что в случае setInterval(), что в случае setTimeout(). Заметьте, что для JS нет никакой разницы между «вызов лежит в стеке и ждет» и «вызов еще не упал в стек», и из JS вы не определите, произошло первое или второе. А произойти, в зависимости от обстоятельств, может и то, и другое.
Если браузер не занят выполнением кода, новый вызов в стек выполнения упадёт через <delay> миллисекунд для setInterval() от прошлого срабатывания, и в момент выполнения повторного setTimeout() плюс <delay> миллисекунд.
Если код перезапускает setTimeout() немедленно после срабатывания прошлого — никакой наблюдаемой разницы выполнения между этим случаем и использованием setInterval() — не будет.
Скажите, в неактивной вкладке будет выполняться setInterval? А setTimeout?
Зациклив setTimeout такая ситуация невозможна в принципе.
и могут не уложиться во время отведённое им в setInterval, и будет ещё один вызовНет, не будет, т.к. «тяжеловесные операции» заблокируют поток.
Вы же не думаете что
setInterval(fn, 1)
запущенный ровно на одну секунду сложит в event pool 1000 вызовов и потом их будет долго разгребать и выполнять? Или думаете? Или думаете, что не будет, но только из-за того что 1 мсек слишком маленький квант?Вы же не думаете что setInterval(fn, 1) запущенный ровно на одну секунду сложит в event pool 1000 вызовов и потом их будет долго разгребать и выполнять? Или думаете?
Вот я только сейчас понял, что наверное заблужение evil_random как раз где-то тут.
Но разумеется, это именно что заблуждение — постановка следующего вызова функции setInterval() в стек выполнения заведомо не начнётся до того, как уже лежащая в стеке функция того же setInterval() не отправится на выполнение. Это явно вытекает из документации на setInterval(), которая гарантирует соблюдение задержки между двумя соседними вызовами функции.
Например, согласно MDN:
This works in a very similar way to setTimeout(), except that the function you pass as the first parameter is executed repeatedly at no less than the number of milliseconds given by the second parameter apart, rather than once.
Не знаю, как там в хроме, но думаю, что абсолютно всё так же. И в прочих браузерах.
PS: А, ну разумеется, это в HTML Living Standard именно так.
Я прекрасно осознаю как работает JavaScript на низком уровне.
И если Вы посмотрите на мой пример ниже, Вы увидите что есть именно принципиальная разница в реализации условной функции ping.
Но почему-то мне чаще всего пишут верхнюю с использованием setInterval.
И если Вы посмотрите на мой пример ниже, Вы увидите что есть именно принципиальная разница в реализации условной функции ping.
Да. Ваша «принципиальная» разница в реализации вашего примера называется «функцию wrongPing написал программист, сознательно руками (а не применением setInterval) забивающий страницу бесконечным количеством таймеров».
Я вам уже написал — если вы сами сделали код, который забивает страницу растущей вычислительной нагрузкой (через стек вызовов ли, через таймеры ли, через DOM ли, через воркеры ли — неважно), то не надо утверждать, что всё это из-за setInterval(). Всё это из-за того, что вы так написали код.
Вас бы я не взял :)
Не обольщайтесь, я бы к вам и не пошел. Люди, которые с апломбом делают некорректные заявления, и используют их на собеседованиях — это огромный красный флаг.
У вас какие-то комплексы что-ли. Где вы апломб увидели? Я написал вопрос, и потом ниже признал, что не совсем корректно поставил задачу. Почитайте внимательно.
Это отличный вопрос на самом деле. В зависимости от ответа можно многое сказать о составителе.
Вам бы я советовал поработать над Soft Skills. Пригодится и в жизни.
Первый и наиважнейший софт скилл — никогда, никому, ни при каких обстоятельствах не советовать «поработать над софт скиллз».
Ваш К. О.
Люди, которые с апломбом делают некорректные заявления, и используют их на собеседованиях — это огромный красный флаг.
А если я «с апломбом делаю некорректные заявления, но использую их исключительно вне собеседований» — нормально?
:)
Если вы с апломбом делаете некорректные заявления вне собеседований, то я уверен найдется у любителей вешать ярлыки и для вас ярлычок :)
:)
Но мне почему-то на собеседованиях очень редко отвечают про eventLoop. И про то, что пишете Вы. Такому ответу я бы только обрадовался.
Что браузер может накапливать setInterval?
Браузер может (и будет) накапливать любые вызовы в стеке выполнения, если ваш код написан так, что он туда их складывает. Неважно, делаете ли вы setInterval() или что-то еще, набивающее вызовы в стек. Как я написал в коде выше, набить вызовы можно и через setTimeout(), для этого нет абсолютно никаких препятствий.
Скажите, в неактивной вкладке будет выполняться setInterval? А setTimeout?
Что будет или не будет выполняться в неактивной вкладке — это вообще дело браузера, а не кода.
Ну и — https://habr.com/ru/post/486820/#comment_21250490
На вопрос вы мне не ответили, кстати.
Что будет или не будет выполняться в неактивной вкладке — это вообще дело браузера, а не кода.
stackoverflow.com/questions/5927284/how-can-i-make-setinterval-also-work-when-a-tab-is-inactive-in-chrome
This can be confirmed by counting the amount of times the setInterval function is called each second — this will not be 30 but just 1 or 2 if the tab is inactive.
Т.е. setInterval отработает столько, сколько ему позволят. И там ничего не сказанно о том, что после активизации setInterval выстрелит всеми невыпонеными итерациями. Точно так же, как и setTimeout в цикле.
const run = function() {
const fun = (counter) => async () => {
console.timeEnd('timeoutFun');
dowork();
if (counter++ < 10) {
timeout(fun(counter));
}
}
timeout(fun(0));
}
Изменим функцию run.
Если я правильно понимаю, между тем как dowork закончит выполняться первый раз и начнет выполняться во второй раз гарантированно пройдет не менее 1 секунды.
Если же распланировать вызовы dowork с использованием setInterval, то это будет гарантировать только то, что между вызовами dowork пройдет не менее 1 секунды.
Т.е. если dowork выполняется 2 секунды, тогда между тем как dowork закончит выполняться первый раз и начнет выполняться во второй раз может пройти менее 1 секунды.
Возможно, evil_random такой кейс хотел описать.
Единственная разница между этими двумя функциями — в пункте 7.3 стандарта про таймеры. Одна перезапустит таймер самостоятельно, вторая — нет.
По факту пошло описание не принципиальных отличий setTimeout от setInterval, а описание отличий планирования перезапусков с помощью setInterval и setTimeout в цикле.
Пару раз мне даже говорили, что не знают для чего нужен setInterval.
const wrongPing = () => {
let counter = 0;
const slowRequestMock = (seqId) => {
const now = new Date();
console.log('Ping', seqId);
setTimeout(
() => console.log('Pong', seqId, now - new Date()),
Math.floor(Math.random() * 3000) + 100,
);
};
setInterval(() => slowRequestMock(counter++), 0);
}
const correctPing = () => {
let counter = 0;
const slowRequestMock = (seqId, nextPing) => {
const now = new Date();
console.log('Ping', seqId);
setTimeout(
() => {
console.log('Pong', seqId, new Date() - now);
nextPing();
},
Math.floor(Math.random() * 3000) + 100
);
};
const ping = () => setTimeout(() => slowRequestMock(counter++, ping), 0);
ping();
}
Понятное дело, что если кандидат начнёт мне рассказывать про eventLoop, я буду только рад и с удовольствием выслушаю его, но чаще всего мне отвечают «ничем».
Мой любимый вопрос на собеседованиях это: «чем отличается setTimeout от setInterval, и чем они будут отличаться, если зациклить setTimeout».
И ответ был вот таким:
Они будут принципиально отличаться.
const absolutelyCorrectPingBecauseSetTimeout = () => {
let counter = 0;
const slowRequestMock = (seqId) => {
const now = new Date();
console.log('Ping', seqId);
setTimeout(
() => {
console.log('Pong', seqId, new Date() - now);
},
Math.floor(Math.random() * 3000) + 100
);
};
const ping = () => setTimeout(() => {ping(); slowRequestMock(counter++)}, 0);
ping();
}
Ой, но как же так? setTimeout, так принципиально отличающийся от setInterval, всё равно забивает страницу бесконечными таймерами?
Вы именно что с апломбом (согласен с JustDont ) пришли в пост, высказали неверное утверждение, а сейчас вьётесь как уж на сковородке.
Сначала у вас было разное поведение setTimeout/setInterval в неактивной вкладке, потом вдруг блокирующие операции (хотя каментом раньше вы писали об анимациях), сейчас вдруг какой-то сломаный пинг. Что это вообще?
setInterval(() => slowRequestMock(counter++), 0);
По существу последнего примера (игнорируя ошибку с
setInterval(fn, 0)
): в вашем примере принципиальное отличие не в setInterval/setTimeout а именно в вашей реализации: вы зачем-то во втором случае реализовали по сути onDoneCallback, а в первом нет. Уберите nextPing()
из тела коллбека setTimeout
в тело slowRequestMock
и разницы не будет. По сути вообще: Вы сказали чушь. Что простительно. При этом, используя фамильярный тон («Вас бы я не взял», «Вам бы я советовал поработать над Soft Skills») Что сомнительно. А теперь вместо того чтобы признать свою неправоту, сливаетесь. Что выглядит просто жалко.
Не хотел бы я оказаться с вами на одном интерьвью. Но вы меня и не возьмёте, я понял. :)
То есть признал, что неверно сформулировал вопрос. Как Вы заметили, все комментарии сохранились.
Я даже привёл примеры как было бы написать верно, а как не верно. Это не вопрос к Senior Developer, как и многие вопросы из этого поста, но ответ на него даже Senior-разработчиком дал бы примерное понимание того, с кем мы имеем дело.
И никуда я не сливаюсь. Я просто не желаю общаться с человеком, который сходу пишет мне в хамско-высокомерном тоне, да к тому же ещё и читает выборочно только то, что ему выгодно. У Вас, к сожалению, тоже есть такая проблема.
Вас бы, кстати, я возможно и взял, ведь вы сразу попробовали воспроизвести пример, без хамства и занудства. Просто не верно меня поняли.
А по поводу придирок к словам, так можно придраться и к комментариям которые были выше «браузер не занят выполнением кода» и начать нести что-то в стиле «вообще-то браузер никакого кода и не выполняет вовсе, потому что тупо не может, его выполняет JavaScript Engine, V8 в случае Хрома и SpiderMonkey в случае Firefox», но я намеренно упростил разговор заявив «setInterval запускает код». Конечно же код запускает не он, код запускает процессор вообще.
Даже если подойти чисто формально, то setTimeout и setInterval имеют принципиальное отличие в том, что один запускает код, простите, кладёт в стек v8, ссылку на то, что вы ему там передали, один раз, а второй делает это каждые N ms.
Но я уже понял, что конструктивного диалога у нас не получится по выше озвученным причинам. Цель Джастонта не развеять мои заблуждения (хотя их и нет вовсе), а показать что в интернете кто-то не прав. Это чётко прослеживается по его ранним комментариям к другим постам. Любит человек это дело. Что поделаешь.
Это без меня, пожалуйста.
То что я его не совсем корректно поставилНе «не совсем корректно», а «совсем не корректно». Таким он и продолжает быть после третьей итерации.
Я просто не желаю общаться с человеком, который сходу пишет мне в хамско-высокомерном тонеЕго тон был вполне справедливым ответом на ваше высокомерие вкупе с высказанным заблуждением ещё здесь:
Вообще, в зависимости от того, как мне отвечают на этот вопрос я понимаю знает ли человек принципы работы JavaScript или сразу с React начал.
Мне 36 лет и моя основная специализация с 2007 года — javascript. И я до сих пор ещё ни разу не собеседовал джунов, представьте. Именно потому что всё ещё не уверен на 100% в своих силах. И очень не хочу выглядеть так, как вы сейчас. Это большая ответственность. Не перед компанией за то, что провороните ценный кадр, а перед соискателем. Он видит в вас авторитет. А вы ему не соответствуете в должной мере.
Для того чтобы собеседовать джунов не нужно полной уверенности в своих силах. Я, как правило, смотрю есть ли в человеке черты присущие инженеру. Если есть, то он быстро из джуна мидлом станет и пойдёт дальше.
Вопрос вполне корректен: «1) в чём отличие setInterval от setTimeout? 2) если вместо setInterval взять и зациклить неблокирущий код в setTimeout, чем будут отличаться реализации?».
Мне 46, в индустрии с 1992, и я кого только не собеседовал. Но я никогда в жизни не доверил бы разговор с соискателем человеку, способному очно, или заочно, произнести фразу «вас бы я не взял».
Я даже кандидатам, которым мы вынуждены отказать, очень вежливо и подробно объясняю, что в этот раз пошло не так, и что сделать, чтобы в следующий раз пошло лучше.
На интервью категорически нельзя задавать вопросы в невнятных формулировках, или имеющие неочевидный ответ. Ответ должен быть либо пуленепробиваемым, не допускающим никаких разночтений или ответа не должно существовать вовсе, тогда мы сможем поговорить ни о чем и оценить, как кандидат думает.
А вот barkalov как раз лукавит, или — если нет — ему давно пора вести собеседования. С таким подходом даже суперскиллы в предмете не нужны, сразу видно — интервьюер отличный.
А то у нас многие кому за 30 переживают, что окажутся никому не нужны совсем скоро. У меня тоже бывают такие мысли.
У меня подробно заполнен профиль, если это так интересно.
Я получаю примерно три-шесть предложений в месяц в свой почтовый ящик напрямую от CEO (HR-спам я выбрасываю, не читая). Не вижу никаких причин для того, чтобы что-то поменялось в будущем.
Даже тут, на Хабре, недавно где-то была статья, что предпочитают молодых-зелёных.
Сеют панику в рядах старожил.
Вы у меня только что один страх убрали. Спасибо :)
На галерах — возможно. В стабильном бизнесе — я никогда не сталкивался с претензиями к возрасту. Мы в прошлом месяце взяли джуна под сорок и QA — моего ровесника.
А еще, я выбрасываю, не читая, резюме людей, которые младше 30, но претендуют на звание «señor» (если у них нет гитхаба, сравнимого с моим, или другого кода показать, конечно).
Я никогда не работал на галерах и надеюсь что никогда не буду вынужден.
Однажды 20-ти летние юнцы с ЕПАМа реджектнули меня с формулировкой «на собеседовании хвастался обширными знаниями в смежных технологиях». Как же я ржал.
По поводу кода я даже не знаю. Мне вот и показать нечего, последние три компании где я работал имели достаточно жёсткие NDA и весь код был закрытый.
Конечно же javascript не real-time и ничего в нём не происходит гарантированно, но в условиях сферического сайта в браузере можно говорить о примерной «гарантии» того, что код будет запущен с помощью setInterval.
Вам не нравится вопрос? Почему?
Хотя я разок так попал, меня уверяли что process.nextTick() выполнится на следующем тике, а setImmediate() на текущем, хотя в ноде это широко известный прикол, что все наоборот.
for(;;) setTimeout(fn);
Поправишь интервьюера — плохо, не поправишь — тоже плохо. Ужасный вопрос, как ни крути.
Интересно, почему везде, где поясняют промисы — делают это так поверхностно? Просто мой любимый случай, это когда есть, например, чейн из 5 then, и тебе надо в пятом то, что вычислилось во втором. Многие начинают в такой ситуации вкладывать промисы друг в друга (для общего скоупа) и… тем самым пораждают тот же самый "колбек хелл", только теперь это "промис хелл" с теми же вложенностями.
Немного распишу, раз уж заплюсовали.
НЕ надо решать так:
- общим скоупом или вложенностью
- передачей всех результатов через все звенья
Пример плохого решения:
first().then(resultA => {
return second().then(resultB => {
return third(resultA, resultB)
})
})
Решать надо переиспользованием объектов промисов, которые несут в себе значение. Примерно так:
const a = first()
const b = second()
Promise.all([a, b]).then(results => {
const [resultA, resultB] = results
return third(resultA, resultB)
})
const b = a.then(second);
иначе примеры не эквивалентны.А решать всё-же лучше с помощью await.
const a = await first();
const b = await second();
return await third(a, b);
Ах да, вы, безусловно, правы. Просто писал в спешке. Асинк/эвейт хорошо, но это не всегда эквивалентно коду выше, так как чисто на промисах вы все таки не блокируете данный стэк, а едете дальше в исполнении кода. Разница не большая, но она есть.
Или вы имеете ввиду это:
const a = first();
const b = second();
// "едем дальше"
return await third(await a, await b);
То это как раз не эквивалентно изначальному «плохому» примеру на промисах.
Async/await это сахар из сборной солянки из генераторов и промисов. Разница только в том, что пример, где Promise.all — не блокирует последующее выполнение в данном скоупе (просто представьте, что у вас тысяча и одна асинк операция в одном скоупе — лишними эвейтами вы будете блокировать даже делегацию задач на low-level API или регистрацию каких-либо коллбеков. Возможно пример из ноды вам прояснит что я имел ввиду:
const read = util.promisify(fs.readFile)
// scope-blocking
(async () => {
console.log(await read('./one.txt'))
console.log(await read('./two.txt'))
console.log(await read('./three.txt'))
})()
// non-blocking
Promise.all([
read('./one.txt'),
read('./two.txt'),
read('./three.txt')
]).then(results => {
console.log(results[0])
console.log(results[1])
console.log(results[2])
})
// scope-blocking
(async () => {
let one = read('./one.txt');
let two = read('./two.txt');
let three = read('./three.txt');
let results = await Promise.all([one, two, three]); // blocking here
console.log(results[0]);
console.log(results[1]);
console.log(results[2]);
// some code here that can't be reached until results resolved
})()
Вы об этом, что в вашем случае console.log'и будут выполнены в отдельном коллбэке, а в моем случае в основном теле функции, и, соответсвенно, для этого await в моем случае подождет резолва промисов, и не побежит по коду дальше.
Тогда согласен с вами, только это не вина и не проблема async, просто к нему нужен чуть другой подход. Саму функцию можно декомпозировать на части так, чтобы «дожидание» не мешало другому коду, которому это не требуется. По сути
именно по этому у вас console.log в коллбэке — это и есть та же декомпозиция, просто не такая красивая.
Ну и Promise.all, понятное дело, я выкидывать не призываю, он и с async\await бывает нужен, когда нужен.
let scopedResultA, scopedResultB;
first().then(resultA => {
scopedResultA = resultA;
}).then(()=>{
return second().then(resultB => {
scopedResultB = resultB;
})
}).then(()=>{
return third(scopedResultA, scopedResultB);
})
Да, и последовательные await именно так и работают. Каждый await создаёт скрытый коллбек, который получает скоуп предыдущего.
А идея переиспользовать промисы конечно изящная, но на практике не сказал бы что это улучшает читабельность. Хотя я часто храню данные в промисах, чтобы гарантировано запустить код после, например, инициализации конфига.
это же и есть общий скоуп, который автор вопроса советует избегатьНе вижу ничего плохого в общем скоупе на async\await. Просто именно на промисах общий скоуп выглядит уродливо. Как впрочем и вложенные промисы. Потому мы и любим async/await.
Я за это:
async function doSome() {
const a = first();
const b = second();
const c = third(await a, await b);
// if u afraid that await will 'block the scope'
// just do not place code here that must run berfore a and b resolves
// place it in main() instead - it's a pretty common sense
return await c;
}
async function main() {
const c = doSome();
doSomeElse(); // run "parrallel" task here
return await c;
}
const a = await first();
const b = await second();
не эквивалентно
const a = first();
const b = second();
Promise.all([a, b])
68. Как бы Вы реализовали вспомогательную функцию запоминания?Очень плохой пример. В вашей имплементации объект
cache
, (как и все объекты) может иметь только строковые имена (ключи) свойств. Либо делайте мемоизацию с помощью Map
, либо не делайте вовсе. Так как:let a = memoizeReduceAdd([1, 2, 3, 4], 5); // a = 15
let b = memoizeReduceAdd([1, 2, 3, 4], 5); // b = 15
let a = memoizeReduceAdd(['1', '2', '3', 4], 5); // a = "51234"
let b = memoizeReduceAdd([1, 2, 3, 4], 5); // b = "51234", кэш "испорчен"
11. Почему obj.someprop.x приводит к ошибке?Вопрос из разряда «почему индейка перешла через дорогу». Как вам ответ «потому что там прописано кидать ошибку», например?
let obj = {someprop: {}};
Object.defineProperty(obj.someprop, 'x', {get: () => {throw "Error!"}});
Или, например, «потому что API отдаёт в нём значения за пределами допустимого диапазона»?14. В чем разница между операторами "==" и "==="?Сначала упоминание toPrimitive(), toString() и valueOf(), а потом таблица без единого явного использования этих функций. Почему бы добавить туда такие варианты?
x = { valueOf: () => 10};
y = 10;
x = { toString: () => 20};
y = 20;
16. Для чего используется оператор "!!"?С каких пор
!!
стал отдельным оператором? Это не ++
и не --
, это просто дважды подряд применённый унарный оператор !
.Нельзя создавать функции с помощью функции eval:Во-первых, где тут создаётся функция?
eval('var x = 1')
Во-вторых, что-то я не вижу, чтобы с помощью eval нельзя было создавать функции:
(function () {
'use strict'
eval('function test(){console.log("Success");};test()');
})();
-> Success
(function() {
'use strict'
let a = eval('(function (){console.log("Success");})');
a();
})();
-> Success
В 30-м воопросе вообще не JS, а JSX. К чему в вопросах по чистому JS внезапно из ниоткуда JSX-код в качестве примера?
Этот код вообще не распарсится, там же аж подсветка синтаксиса сломалась.const three = function three({ return arguments })
Переменные, объявленные с помощью ключевого слова «var», являются глобальными. Это означает, что они доступны из любого места в коде:Вот прямо-таки из любого, а не только из тела функции, в которой они объявлены?
function lol() {
var myX = 10;
}
lol();
myX
->ReferenceError: myX is not defined
eval()
функций: Я вот только на днях узнал, что Function(param)
/ new Function(param)
может принимать в качестве param
— строку с кодом тела функции. То есть как eval
, но создает функцию.Так?
new Promise((resolve, reject)=> { reject(new RangeError('pizza size is too big'))});
Режект промиса не выкидывает эксепшн, если мы не внутри асинка.
А задачка решалась через генераторы. Генератор имеет метод throw
без использования throw
А задачка решалась через генераторы. Генератор имеет метод throwПоясните, какой смысл в этом методе, если по условиям его нельзя использовать?
(async () => {})()
, то решение верное? Если так, то я собой доволен.А по-поводу никаких «eval или Function», как вам такое:
setTimeout('console.log("there is one more way to evaluate string")', 0);
Функциональное программирование — это прежде всего ленивость, иммутабельность и хвостовые рекурсии.
ФП — это про чистые функции и ФВП. А то, что вы называете, это, по-моему, свойства каких-то знакомых вам языков или систем.
Ленивость — оптимизация, иммутабельность не требование, хвостовая рекурсия — частный случай рекурсии, который легко поддаётся о оптимизации. Хорошо хоть про систему типов не вспомнили
Основная идея ФП — что мы не изменяем состояние. Отсюда — иммутабельность.
Это значит что традиционные конструкции процедурных языков заменяются функциональными аналогами.
Аналогом оператора присваивания становится создание функции по вычислению переменной. Это удобно сделать с помощью ленивости. Тогда можно писать a=b, подразумевая что здесь мы создаём новую функцию.
Аналогом же цикла является хвостовая рекурсия.
Типичным примером функционального языка является хаскель. Ни Java Script, ни Scala функциональными языками не являются.
А по поводу ФВП, то они вообще чуть ли не в каждом языке есть, что ж тогда все языки функциональными называть?..
Передача коллбака в функцию на С — обычное тривиальное действие. С — теперь функциональный язык?
ФП — это то что пошло из лямбда исчислений.
LISP принято относить к мультипарадигменным функциональным языкам, вообще-то.
Основная идея ФП в том, что мы оперируем чистыми функциями, есть у них внутренние переменные/состояние, циклы или нет — нюансы реализации самой функции, пока на один и тот же набор параметров она отдаёт один и тот же результат.
И почему именно хвостовая? Какая религия мешает делать любую рекурсию для итерирования? Я вот предполагаю, что лишь то, что трансляторы могут эффективно разворачивать хвостовую рекурсию в циклы.
Все (или почти все) популярные языки являются мультипарадигменными. И вообще мы вроде о функциональных языках не говорили, а говорили о функциональном программировании.
Какая религия мешает делать любую рекурсию для итерирования?
Стек мешает :) TCO зачищает стек за собой, а нехвостовой рекурсивный вызов так не сможет, потому что вызывающая функция еще не закончилась, и ей стек может пригодиться. Попробуйте в любом языке, который поддерживает TCO (не js) факториал чего-нибудь значимого посчитать так и эдак. Ну вот, например:
def f!(n, acc = 1)
return acc if n <= 1 1
f!(n - 1, n * acc)
end
f!(100_000).to_s.size
#⇒ SystemStackError: stack level too deep
но:
RubyVM::InstructionSequence.compile_option = {
tailcall_optimization: true,
trace_instruction: false
}
def f!(n, acc = 1)
return acc if n <= 1 1
f!(n - 1, n * acc)
end
f!(100_000).to_s.size
#⇒ 456574
Интересно, что виртуальная машина эрланга внезапно умеет сделать TCO из кривого кода везде, где это возможно.
defmodule F do
def f!(1), do: 1
def f!(n) when n > 0 do
prev = f!(n - 1)
prev * n
end
end
100_000 |> F.f!() |> to_string() |> String.length()
#⇒ 456574
Вопрос 31.
Объект Array содержит методы map, filter и reduce, которые являются самыми известными функциями в мире функционального программирования из-за их полезности, а также потому, что они не изменяют массив, что делает эти функции «чистыми».
Вот здесь вообще есть нюанс про то, что это справедливо для массивов с примитивными типами данных. Если на входе есть массив объектов, то в том же .filter или .map никто не запрещает как угодно модифицировать элементы массивов и никакой иммутабельности в этом случае не будет
18. Что такое поднятие (Hoisting)?
Компиляция. В этой фазе функциональные выражения и переменные, объявленные с помощью ключевого слова «var», со значением undefined поднимаются в самый верх глобальной (или функциональной) области видимости (как бы перемещаются в начало нашего кода.
Запомните: поднимаются только функциональные выражения и переменные, объявленные с помощью ключевого слова «var». Обычные функции и стрелочные функции, а также переменные, объявленные с помощью ключевых слов «let» и «const» не поднимаются.
Функциональные выражения не поднимаются. А «обычные функции», то есть объявления функций наоборот поднимаются.
Откорректируйте, пожалуйста, ответ по 18-му вопросу.
«Функциональные выражения в JavaScript не поднимаются (hoisting), в отличие от объявленных функций.»
developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/function
Статья огонь! Не совсем понял про 56 вопрос. Вы пишите, что isNaN работает странно и не совсем так как надо.
Результатом всех console.log является true, несмотря на то, что ни одно из значений не является NaN
По мне так isNaN работает верно и все что у Вас в аргументах функции isNaN действительно НЕ ЧИСЛО и поэтому возвращается true. Проверка же идет на то, что это НЕ число — Not a Number, а не наоборот )) А вот если мы напишем console.log(isNaN(7)) то выведет false, что тоже верно, так как 7 это число! )) Главное не путаться и чётко понимать на что мы проверяем.
Спасибо.
То есть, как это ни странно NaN — формально как раз число.
typeof(NaN); // "number"
Грубо говоря, функция
isNaN(value)
эквивалентна (value === NaN)
, если бы не одно но: NaN неравен сам себе, поэтому-то и запилили эту функцию.В вопросе 23 про 'use strict' поправьте ошибку, пожалуйста:
Object.defineProperties(obj, 'x', { value: 1 }) delete obj.x // Uncaught TypeError: Property description must be an object: x</code>
В этом примере ошибка вызвана не наличием 'use strict', а неправильной записью, о чем и говорится в сообщении: Property description must be an object: x. А вот с записью:
Object.defineProperties(obj, {'x': {value:1}})
delete obj.x // Cannot delete property 'x' of #<Object>
ошибка будет связана непосредственно с 'use strict'
нужно уточнить:
в строгом режиме значение this для глобального объекта windows равно undefined
var o = {
name: 'myObject',
getName: function getName(){ return this.name }
}
window.name = 'lalala';
window.getName = o.getName
console.log(window.getName()) // VM294:6 Uncaught TypeError: Cannot read properties of undefined (reading 'name')
42. В чем разница между ключевыми словами «var», «let» и «const»?
Переменные, объявленные с помощью ключевого слова «var», являются глобальными.
42. В чем разница между ключевыми словами «var», «let» и «const»?
Переменные, объявленные с помощью ключевого слова «var», имеют функциональную область видимости.
70 вопросов по JavaScript для подготовки к собеседованию