Как стать автором
Обновить

Комментарии 34

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

Всё ок

Все же логично.

0 - это значение.

null - отсутствие значения.
0 != null - верно )

null == 0 // false
null > 0 // false
null >= 0 // true

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

Непростую маленькую табличку приведения типов.
Разумеется. Мало желающих зубрить всякую хрень, которой проще и надёжнее не пользоваться.

Возможно это так и действительно не очень просто, как вижу со мной много не согласных. К сожалению, чтобы профессионально писать на JS нужно это изучать, либо выбрать язык без таких особенностей. Но, как говорил кто-то из великих - "Или язык все ругают, или он не популярный", как-то так там было. Живем как можем, в общем-то :)

Но если уж нельзя никак отвертеться и вам нужно писать на JS, хоть и не хотите - есть лайфхак как никогда не встречаться с особенностями этих механизмов - всегда в ручную использовать приведение типов перед любыми операциями, а также для сравнения использовать тройное равно. Бонусом могут быть линтеры, ну и TypeScript.

К сожалению, чтобы профессионально писать на JS нужно это изучать, либо выбрать язык без таких особенностей.

Та ну ерунда какая. Я пишу на JS уже больше 10 лет. Я не знаю таблицы приведения типов. И мне ни разу не стыдно. Чему равен [] + {}? Понятия не имею, и иметь не хочу. Какой-нибудь '[object Object]` или что-то типа того. И в мои планы её зазубривать не входит. Рецепт простой:


  • никогда не используем ==
  • используем Typescript
  • ставим Eslint правило для TS, которое запрещает неявное приведение типов (скажем в интерполяциях, конкатенациях и мат. операциях).

Вуаля.

К сожалению, чтобы профессионально писать на JS нужно это изучать

Достаточно помнить, что такое есть, на случай если придётся разбираться в чужом коде. А обходить эти проблемы в собственном коде — хитрость невеликая.
Только недавно была статья по python о том же поведении
Надо же прострелить себе и вторую ногу!

Ну да, давайте теперь про каждый язык где оно так же работает по статье сделаем. Java, Kotlin, C#, Ruby, PHP... Где там ещё есть аналог finally?

В C# не работает, надо статью по этому поводу.

Действительно, в C# вообще запрещён return внутри finally. Ну раз запрещён, то и нет вопросов какой из return'ов победит.

Полагаю, практического применения у вышеописанной особенности return нет. 

try {
  return some()
} catch( error ) {
  console.log( error )
  return 'default'
}

В вашем примере только один из операторов return выполняется.

И ещё — прошу вас — не задавайте вопросов об этом на собеседованиях.

А почему, собственно, нет?

На самом деле, вполне могу себе представить реальный кем-то написанный код вроде такого:

function calcSomething() {
  try {
    return funcThatMightFail();
  }
  finally {
    return defaultValue;
  }
}

Причём, я даже вижу как такой код может попасть в прод и жить там продолжительное время - особенно если это не критичный функционал, а, к примеру, расчёт скорости какой-нибудь анимации.

Функция всегда будет возвращать defaultValue

Да, именно так. Я это к тому, что приведённый выше код, если не вглядываться, выглядит слишком похоже на корректный.

Отсюда и был мой изначальный вопрос, что плохого спросить об этом на собеседовании? На мой взгляд, вполне нормальный вопрос на понимание конструкции try/finally для джуна. На который в ответ, как вы и ваши коллеги в комментариях правильно заметили, хочется услышать про возвращаемое значение, варианты исправления (замена на catch либо введение переменной).

Вдобавок, тут ещё и проброс исключений можно обсудить (например, стоит ли их в принципе вот так вот "отлавливать"). В общем, отличное начало беседы, как по мне.

что плохого спросить об этом на собеседовании?

вполне нормальный вопрос на понимание конструкции try/finally для джуна

В том что вам и senior на него не ответит (в большинстве случаев). Потому что это контр-интуитивный нюанс языка, которых люди стараются избегать. И сразу забывать. Может быть полезным в каких-то случаях. Но чаще это мёртвый груз.


Во время собеседования у вас мало времени. И вам нужно выяснить — будет ли этот человек полезным звеном в вашей команде? Сможет ли он решать поставленные перед ним задачи? Насколько он самостоятельная боевая единица?


И вопросы про finally-return примерно из той же оперы что и "реализуйте вручную асинхронный итератор для заданного объекта". Вроде и полезный навык, но больше из области кругозора и любопытства. На вопросы выше не отвечает.

На это есть catch блок

Сам споткнулся на этом, когда переписывал код типа:

let result
try {
	result = ...
} finally {
  result = ...
}
return result

В общем, под жареным заголовком автор описал поведение finally. А return приплёл чтобы пожонглировать словами.

В promise finally не влияет на результат promise, MDN, это просто код, который выполняется и при reject и при resolve.

Очень много некорректных выражений в статье (побеждает последний return, finally очищает ошибки).

Пишешь грязный код - получишь грязный результат

if (file_exists($filename)) {
    die();
} else {
    include $filename;
}

При определенных обстоятельствах, может выполниться команда include...

Как хорошо, что 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, а вовсе не промис "обёрнутый" в функцию.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.