Комментарии 35
TL;DR если в JS-коде есть блок try-finally, содержащий return, code flow имеет последовательность try -> finally -> return (try) если он там был, что приводит к возврату значения из блока finally, если там есть отдельный return.
А вот плюшка про Promise поинтереснее, там code flow не настолько очевиден, тем более промисы по дизайну асинхронные.
Нам, JS-ерам и так херово живется, а еще ты тут с магией
На еще тебе
null >= 0
null > 0
5 >= 5 // true
5 > 5 // false
Всё ок
5 == 5 // true
null == 0 // false
Вы не уловили суть ;)
Все же логично.
0 - это значение.
null - отсутствие значения.
0 != null - верно )
null > 0 // false
null >= 0 // true
Десять лет всё надеюсь что люди выучат простую маленькую табличку приведения типов. Но нет, лучше забить и каждый раз страдать, удивляясь почему так.
Разумеется. Мало желающих зубрить всякую хрень, которой проще и надёжнее не пользоваться.
Возможно это так и действительно не очень просто, как вижу со мной много не согласных. К сожалению, чтобы профессионально писать на JS нужно это изучать, либо выбрать язык без таких особенностей. Но, как говорил кто-то из великих - "Или язык все ругают, или он не популярный", как-то так там было. Живем как можем, в общем-то :)
Но если уж нельзя никак отвертеться и вам нужно писать на JS, хоть и не хотите - есть лайфхак как никогда не встречаться с особенностями этих механизмов - всегда в ручную использовать приведение типов перед любыми операциями, а также для сравнения использовать тройное равно. Бонусом могут быть линтеры, ну и TypeScript.
К сожалению, чтобы профессионально писать на JS нужно это изучать, либо выбрать язык без таких особенностей.
Та ну ерунда какая. Я пишу на JS уже больше 10 лет. Я не знаю таблицы приведения типов. И мне ни разу не стыдно. Чему равен [] + {}
? Понятия не имею, и иметь не хочу. Какой-нибудь '[object Object]` или что-то типа того. И в мои планы её зазубривать не входит. Рецепт простой:
- никогда не используем
==
- используем Typescript
- ставим Eslint правило для TS, которое запрещает неявное приведение типов (скажем в интерполяциях, конкатенациях и мат. операциях).
Вуаля.
К сожалению, чтобы профессионально писать на JS нужно это изучать
Достаточно помнить, что такое есть, на случай если придётся разбираться в чужом коде. А обходить эти проблемы в собственном коде — хитрость невеликая.
Ну да, давайте теперь про каждый язык где оно так же работает по статье сделаем. Java, Kotlin, C#, Ruby, PHP... Где там ещё есть аналог finally?
Полагаю, практического применения у вышеописанной особенности
return
нет.
try {
return some()
} catch( error ) {
console.log( error )
return 'default'
}
И ещё — прошу вас — не задавайте вопросов об этом на собеседованиях.
А почему, собственно, нет?
На самом деле, вполне могу себе представить реальный кем-то написанный код вроде такого:
function calcSomething() {
try {
return funcThatMightFail();
}
finally {
return defaultValue;
}
}
Причём, я даже вижу как такой код может попасть в прод и жить там продолжительное время - особенно если это не критичный функционал, а, к примеру, расчёт скорости какой-нибудь анимации.
Функция всегда будет возвращать defaultValue
Да, именно так. Я это к тому, что приведённый выше код, если не вглядываться, выглядит слишком похоже на корректный.
Отсюда и был мой изначальный вопрос, что плохого спросить об этом на собеседовании? На мой взгляд, вполне нормальный вопрос на понимание конструкции try/finally для джуна. На который в ответ, как вы и ваши коллеги в комментариях правильно заметили, хочется услышать про возвращаемое значение, варианты исправления (замена на catch либо введение переменной).
Вдобавок, тут ещё и проброс исключений можно обсудить (например, стоит ли их в принципе вот так вот "отлавливать"). В общем, отличное начало беседы, как по мне.
что плохого спросить об этом на собеседовании?
вполне нормальный вопрос на понимание конструкции try/finally для джуна
В том что вам и senior на него не ответит (в большинстве случаев). Потому что это контр-интуитивный нюанс языка, которых люди стараются избегать. И сразу забывать. Может быть полезным в каких-то случаях. Но чаще это мёртвый груз.
Во время собеседования у вас мало времени. И вам нужно выяснить — будет ли этот человек полезным звеном в вашей команде? Сможет ли он решать поставленные перед ним задачи? Насколько он самостоятельная боевая единица?
И вопросы про finally-return примерно из той же оперы что и "реализуйте вручную асинхронный итератор для заданного объекта". Вроде и полезный навык, но больше из области кругозора и любопытства. На вопросы выше не отвечает.
На это есть catch
блок
В общем, под жареным заголовком автор описал поведение finally. А return приплёл чтобы пожонглировать словами.
Так было ж, вот компактно без воды:
https://habr.com/ru/company/mailru/blog/335292/#kovarnyy-trycatch
Пишешь грязный код - получишь грязный результат
Как хорошо, что Blazor вот-вот убьет JavaScript и нам больше не придётся страдать!
Пример жуткой безграмотности. А jake archibald несмотря на то что выступает как Google Developer Advocate уже не первый раз замечен в том, что несет ахинею.
Война return
Достаточно открыть спецификацию ECMA и прочитать ее относительно того что такое return Statement следом что такое try statement чтобы увидеть и понять поведение директивы (statement) return для этого случая. (Правильнее говорить не поведение дерективы, а поведение Jit или компилятора, потому что деректива управляет именно им)
А не вводить всех в заблуждение относительно того, что дескать у return ов идет война. Никакой войны нет. return это не оператор - это директива (statement), поведение которой зависит от обстоятельств в которых он находится.
Где в случае с try Statement - return в трех блоках ( catch, error, finaly) только устанавливают значение которое возвращается по завершению.
Пример с промисами
Если в первом случае еще можно было бы попытаться притянуть за уши попытку иронии на тему, не до конца интуитивно понятного синтаксиса в JS, то в этом случае откровенная безграмотность.
Правда,
promise.finally()
оказывает влияние на то, когда именно промис будет разрешён:
const wait = (ms) => new Promise((r) => setTimeout(() => r(), ms));
const promise = Promise.resolve('one').finally(async () => { await wait(2000); return 'two'; });
В данном случае
promise
, всё так же, разрешается значением'one'
, но на это у него теперь уходит две секунды.
Я сначала подумал что это некорректный перевод. Однако в оригинале тоже самое:
In this case
promise
still fulfils with'one'
, but it takes two seconds to do so.
Что является совершеннейшей неправдой.
Даже не заглядывая в документацию относительно того, что возвращают те или иные методы Promise, проведя простой эксперимент увидеть:
const wait = (ms) => new Promise((r) => setTimeout(() => r(), ms));
const promise = Promise.resolve('one');
promise.finally(
async () => {
await wait(2000);
console.log("Finaly")
}
);
promise.then(
(e) => console.log(`Fulfils: ${e}`)
)
то что наш промис разрешился значением one сразу же как к нему пришла в очередь в первом же блоке микротасков, а выполнение finaly произошло запланировано спустя не менее 2000мс, что никак не повлияло на то, когда разрешился промис не повляло.
Автор не в курсе, что async function возвращает сама обьект Promise, а не функцию, который попадает как значение, а не функция в метод finaly, что в таком случае заменяется на .finaly( () => Promise ); То есть значение оборачивается в функцию которая и становится cb для finaly которая возвращает промис.
Игого:
Как я говорил уже выше, Jake уже не первый раз садиться в лужу. И что хуже всего, он имеет достаточный авторитет для многих людей которые доверяют ему без вопросов, что очень зря даже в основах JS, что на примере выше наглядно видно.
А что это вы исследуете исходный промис, а не тот, который вернул метод finally? Автор-то про второй писал (что очевидно в тексте, но не очевидно в выдернутой вами из контекста цитате).
По двум простым причинам:
1) В статье прямо сказано что "promise", то есть тот который был задан во время создания ( название переменной).
const promise = Promise.resolve('one');
Если предположить что автор ошибся и забыл поставить заглавную букву, то есть имея ввиду не ссылку promise которую он обьявил выше, а Promise вообще как класс то причина номер два, описанная в мое посте:
2) обьявляя async function он передал в finaly не функцию, а новый обьект Promise, который согласно спецификации как и любое другое значение отличное от функции оборачивается в () => переданной значение.
которое в случае примера из статьи равно созданному промису для функции, но не вызову функции.
то есть его код для наглядности можно переписать вот так
const testFuncPromise = new Promise (
(res, rej) => {
setTimeout( {console.log("Finaly"); res(); } , 2000)
});
promise.finally( () => testFuncPromise );
То есть как только он написал в своем коде async () => { // come code } он передал в finaly не функцию () => { // come code }, которая выполниться в нужный момент, но новый промис, который был автоматически завернут в новую созданную автоматически функцию () => { return (Promise созданный при объявлении async) }
Во-первых, в статье явно указано название переменной, и приведён кусок кода:
const promise = Promise.resolve('one').finally(async () => {
await wait(2000);
return 'two';
});
Вызов finally тут в той же строчке не для красоты сделан.
Во-вторых, async function всё ещё function, которая возвращает Promise, а вовсе не промис "обёрнутый" в функцию.
В JS-функциях «побеждает» последний оператор return