спробуй ловити в JavaScript ... це не найкраща практика?


69

Існує положення про блок -пробний блок в JavaScript . Хоча в Java або будь-якій іншій мові обов'язково обробляти помилки, я не бачу, щоб хтось використовував їх у JavaScript в більшій мірі. Це не є хорошою практикою чи просто нам вони не потрібні в JavaScript?


2
While in java or *any other language* it is mandatory to have error handling...- Не зовсім. Java, так, але є багато мов, які не наполягають на пробному лові (як, наприклад, C #).
Джим Г.

Це тому, що ви не можете використовувати їх у середовищі асинхронізації. Я часто використовую їх за допомогою коду синхронізації нижчим рівнем абстракції, наприклад, перетворюючи щось на щось тощо.
inf3rno

Б'юсь об заклад, що у коді на сервері ви побачите більше спроб лову, ніж на стороні клієнта. Більшість кодів, що знаходяться на стороні вільної сторони, не мають нічого важливого. Я б став до мінімуму, що мінімізація KB вниз є важливішою, ніж відновлення після кожної помилки - у більшості програм та обставин, які стосуються браузера.
svidgen

Є ще один спосіб уникнути спроби ловити, використовуючи, можливо, і в Either (монадах) у нас був веб-скребок, написаний у вузлі. Нам вдалося видалити весь спробу лову, використовуючи його.
user93

Відповіді:


66

Слід уникати throwпомилок як способу передачі умов помилок у додатках.

throwЗаява має бути використано тільки «Для цього не повинно статися, аварії і спалити. Чи не відновлюйте елегантно в будь-якому випадку»

try catch однак використовується в ситуації, коли хост-об'єкти або ECMAScript можуть видаляти помилки.

Приклад:

var json
try {
    json = JSON.parse(input)
} catch (e) {
    // invalid json input, set to null
    json = null
}

Рекомендації у спільноті node.js полягають у тому, що ви передаєте помилки у зворотному звороті (оскільки помилки трапляються лише для асинхронних операцій) в якості першого аргументу

fs.readFile(uri, function (err, fileData) {
    if (err) {
        // handle
        // A. give the error to someone else
        return callback(err)
        // B. recover logic
        return recoverElegantly(err)
        // C. Crash and burn
        throw err
    }
    // success case, handle nicely
})

Є й інші проблеми, такі як пробувати / ловити - це дуже дорого, це некрасиво, і він просто не працює з асинхронними операціями.

Отже, оскільки синхронні операції не повинні видавати помилки, і це не працює з асинхронними операціями, ніхто не використовує спробу вилову, крім помилок, викинутих об'єктами хосту або ECMAScript


2
Я б не пішов так далеко, щоб сказати, що ніхто не використовує спробу лову, в більшості випадків це просто неправильний інструмент для роботи. Коли існують справді виняткові обставини, це може бути варто кинути Error, але їх небагато і між ними.
zzzzBov

7
@zzzzBov Немає нічого поганого в помилках кидання для випадку C, аварії та запису. Я просто не думаю, що вам слід вловлювати помилки та відновити. Наприклад document.getElementById, не кидає, коли елемент не існує, він просто повертається null. Те саме можна зробити майже для всіх випадків
Райнос

4
@Raynos, викид з функції синхронізації цілком прийнятний і має сенс у цьому випадку використання. Повернення помилки до зворотного дзвінка - це асинхронізація, оскільки помилка перекидання - це синхронізація.
sbartell

@Raynos має якісь корисні поради щодо уникнення використання помилок / винятків для контролю потоку на стороні клієнта (несередовище)? Я знайшов цю публікацію в StackOverflow , але в основному орієнтований на Яву
blong

@ b.довго просто. ніколи не кидайте помилок. Проблема вирішена.
Райнос

32

Спробуйте / ловити в Javascript не настільки надійно, як на інших мовах, через асинхронність Javascript. Розглянемо цей фрагмент:

try {
    setTimeout(function() {
        do_something_that_throws();
    }, 1000);
}
catch (e) {
    alert("You won't see this!");
}

Проблема полягає в тому, що контрольний потік залишає tryблок перед тим, як do_something_that_throws()його виконати, тому помилка, викинута всередині зворотного виклику, ніколи не потрапляє.

Тому спроба / лов у багатьох випадках є недоцільною, і не завжди очевидно, чи щось виконує код асинхронно чи ні. На щастя, javascript із своєрідною ідіомою зворотного виклику з однопотоковою асинхронною підтримкою та підтримкою фактичного закриття забезпечує елегантну альтернативу: продовження передачі помилок стилю. Просто передайте належну відповідь на будь-яку помилку як функцію, наприклад:

setTimeout(function () {
    do_something_that_calls_err(function(err) {
        alert("Something went wrong, namely this: " + err);
    }),
    1000);

5
це не є кулезахисними і іншими мовами. Перенесіть цей код на будь-яку мову, що підтримує асинхронні зворотні дзвінки, і він також вийде з ладу.
Райнос

2
@Raynos: Ви маєте рацію; однак, інші мови (а точніше їх підтримуючі культури) не купують це сильно в ідіому асинхронного зворотного виклику, як це робить Javascript, а через багатопотоковість більшості інших середовищ, недоліки спроби / лову є більш очевидними та оточений безліччю інших застережень, тому люди, природно, ретельно ступають у багатопотокових / асинхронних ситуаціях зворотного виклику та знають більше про потенційні підводні камені.
tdammers

Це насправді пов'язано з "асинхронною природою JavaScript"? Наскільки я бачу, Try / Catch теоретично можна зробити так, щоб він працював лексично, як те, як ідентифікатори лексично закриті; просто не трапляється так працювати в JavaScript.
Брайан Гордон

2
У node.js> = 0.8 варто зазначити, що можна спробувати / зловити асинхронно, будь ласка, дивіться моє запитання stackoverflow.com/questions/14301839/…
Бенджамін Груенбаум

Єдиною альтернативою синхронному пробному лову є асинхронний стиль передачі продовження?
CMCDragonkai

23

Багато цих відповідей трохи застаріли і не враховують нових особливостей ES7 asyncта await.

Використовуючи async/ awaitтепер ви можете отримати асинхронний потік управління, як вам потрібно:

async function email(address) {
  try {
    // Do something asynchronous that may throw...
    await sendEmail({ to: address, from: 'noreply@domain.com`, subject: 'Hello' })
  } catch(err) {
    if (err instanceof SomeCustomError) {
      elegantlyHandleError(err)
    } else {
      throw err
    } 
  }
})

Дізнайтеся більше про async/ awaitтут . Ви можете використовувати async/ awaitзараз, використовуючи babel .


Але він схожий на синхронний код
Atul Agrawal

@AtulAgrawal так, в цьому справа. Асинхронізація / очікування дозволяє писати код асинхронізації в синхронному стилі, щоб ви могли уникнути "пекло зворотного дзвінка" та обв'язувати багато обіцянок разом. Дуже приємний спосіб написати асинхронний код на мою думку
Дана Вудман

тож яка користь від використання javascript, якщо ми робимо асинхронний код синхронним. тому що більшість людей використовують javascript через його асинхронний характер
Atul Agrawal

2
@AtulAgrawal ви отримуєте найкраще з обох світів - ви насолоджуєтесь асинхронною природою мови (так як код вище все ще буде виконуватися async - звільняючи потік і все) і позбавляєте переваг більш зручного для програміста стилю кодування. Але не без проблем, хоча ...
ZenMaster

15

try-catch в JavaScript настільки ж дійсний і корисний, як і в будь-якій іншій мові, яка їх реалізує. Є одна основна причина, що його не використовують стільки в JavaScript, скільки в інших мовах. Це та сама причина, що javascript розглядається як потворна мова сценаріїв, це та сама причина, чому люди вважають, що програмісти JavaScript не є справжніми програмістами:

  • Javascript - це неймовірно доступна та поширена мова

Сам факт того, що так багато людей піддаються JavaScript (завдяки єдиній мові, що підтримується браузерами), означає, що у вас там багато непрофесійного коду. Звичайно, є також багато незначних причин:

  • деякі речі в JavaScript є асинхронними і, отже, не в catchзмозі (асинхронні речі)
  • було багато перекритих розмов про те, як пробний лов має величезний хіт на продуктивність. Він трохи вдарив про продуктивність , але для більшості кодів він того вартий.
  • javascript був (на жаль) реалізований таким чином, що часто мовчки ігнорує помилки (автоматично змінюючи рядки на цифри, наприклад)

Незважаючи на те, слід користуватися пробним уловом , але, звичайно, ви повинні навчитися правильно їх використовувати - як і все інше в програмуванні.


3
І чому коли-небудь ти спускався, о, мовчазний потік?
user250878

Домовились. Я вважаю, що прийнята відповідь, як правило, правдива, але є вагомі причини використовувати пробний лов і навіть кидати, крім випадків, коли стосуються рідних об'єктів. Коли помилка передається, а не передається до зворотного дзвінка, вона зупиняє подальше виконання та стрибає стек до першого блоку, ніж може впоратися з ним. Це є великою перевагою і має ідеальний сенс для синхронних операцій, особливо якщо вони передбачають глибоке гніздування. Здається, божевільним вважати, що винятки є "поганими" (ну, крім очевидних причин) - вони насправді є дуже корисним інструментом з унікальною "силою".
напівколонка

2
Ви коли-небудь витрачали час, щоб прочитати вихідний код, скажімо, jQuery? Навряд чи будь-який спробу лову там, за винятком саме тих сценаріїв, згаданих у прийнятій відповіді: для виявлення функцій та використання власних об'єктів / хостів, які видають помилки. Викликати код, який не використовує багато пробного лову (наприклад, jQuery), "непрофесійно" видається нерозумним. Якщо чесно, я думаю, що це особливо нові програмісти Javascript, що надходять з Java, які мають тенденцію до надмірного використання мовних функцій, таких як Try-catch.
Штійн де Вітт

6

Я вважаю, що більша частина причин, що try..catchзустрічається в JavaScript, є в тому, що мова має досить високу толерантність до помилок. Переважна більшість ситуацій може бути вирішена за допомогою перевірки коду, хороших значень за замовчуванням та асинхронних подій. У деяких випадках просто використання шаблону запобігає проблемам:

function Foo() {
    //this may or may not be called as a constructor!!
    //could accidentally overwrite properties on window
}

function Bar() {
    if (!(this instanceof Bar)) {
        return new Bar();
    }
    //this will only work on Bar objects, and wont impact window
}

Деякі основні проблеми на інших мовах, які спричиняють винятки, просто не існують у JS. Передача типів не потрібна переважну більшість часу. Замість цього кращим методом, як правило, є функція перевірки (примусовий виконання певного інтерфейсу):

function doFoo(arg) {
    if (arg.foo) {
        arg.foo();
    } else {
        Bar.prototype.foo.call(arg);
    }
}

З додаванням async/ awaitдо мови try..catchстає все більш поширеним. Обіцяє бути асинхронною формою try..catch, має сенс очікувати:

doSomething().then(
  doSomethingWithResult,
  doSomethingWithError
)

щоб замість цього записати як:

try {
  const result = await doSomething()
  doSomethingWithResult(result)
} catch (e) {
  doSomethingWithError(e)
}

3

Можливо, ще одна причина спроби / лову не дуже використовується в Javascript - це конструкція була недоступною в перших версіях Javascript ... вона була додана пізніше.

Як результат, деякі старі браузери не підтримують це. (Насправді це може спричинити помилку синтаксичного аналізу / синтаксису в деяких старих браузерах, що важче "захистити програму" проти, ніж більшість інших типів помилок.)

Що ще важливіше, оскільки він не був доступний спочатку, вбудовані функції Javascript, які були спочатку випущені (те, що можна було б називати "бібліотечними" функціями на багатьох мовах), не використовують його. (Це не дуже добре, щоб "зловити" помилку з someobject.somefunction (), якщо вона не "кидає", а натомість просто повертає "null", коли виникає проблема.)


Ще одна можлива причина - механізм спробувати / ловити спочатку здається не потрібним (і все ще не здається всім таким корисним). Це дійсно потрібно лише тоді, коли дзвінки звичайно вкладаються на кілька рівнів; просто повернення якихось типів ERRNO спрацювало б добре для прямих дзвінків (хоча, щоб зробити його дійсно корисним завжди, коли воно доступне, найкраща практика більшості мов - це використовувати його скрізь, а не лише при глибоко вкладених дзвінках). Оскільки спочатку очікувалося, що логіка Javascript буде невеликою та простою (зрештою, це лише додаток до веб-сторінки :-), функціональні виклики не очікувались глибокої вкладеності, і тому механізм спробу / лову не видався необхідним.


3
Ні ні ні, абсолютно не так. Двигуни, які старі вже не мають значення на довгий час, а спільнота javascript взагалі швидко скидає старі речі, коли це виправдано. Я б навіть покладався на те, що більшість розробників javascript тепер - IE6.
Каміло Мартін

0

Я вважаю, що вони не так сильно використовуються, оскільки викидання виключень у коді клієнта Javascript ускладнює налагодження сторінки.

Замість того, щоб викидати винятки, я, як правило, вважаю за краще показувати поле попередження (тобто alert("Error, invalid...");)

Це може здатися дивним, але помилки Javascript трапляються на стороні клієнта, якщо клієнти користуються створеною вами сторінкою, а сторінка кидає виняток, якщо замовник не є технічно підкованим кодером, немає ніякого способу він ніколи не зможе сказати Ви в чому питання.

Він зателефонує вам лише сказавши: "Ей, сторінка X не працює!", І тоді все буде вирішувати, що ви знайдете, що пішло не так і де в коді.

Використовуючи натомість поле сповіщення, більш імовірно, що він дзвонить і каже щось на кшталт: "Ей, коли я натискаю кнопку A сторінки X, він показує вікно, на якому написано ..." , повірте, це буде набагато простіше знайти клоп.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.