JavaScript

индекс
246,46

Притча о перехвате ошибок

— Здравствуй, дружок. Хочешь, я расскажу тебе сказку? }:-]
— Конечно, дяденька, а какую? *_*
— Я расскажу тебе как делают детей ^_^'
— ого, как интересно! @_@
— Так вот, слушай. Дети появляются вследствие долгого и изнуряющего процесса разной степени нецензурности. Сейчас я поведаю тебе как появился на свет мой сын %-)
— А вы меня с ним познакомите? *о*
— Не торопи события. Обо всём по порядку. Однажды я наваял такой код:
  1. try {
  2.     throw new Error
  3. } catch( e ){
  4.     if( e != false ) throw e
  5. }
— А что он делает? о_0
— Не важно, главное, тут другое, а именно то, что только Опера и Мозилла в консоли ошибок указали на вторую строку скрипта. Хром указал на четвёрную, а Ишак вообще на третюю .-.
— Ну и что с того? ._.
— А то, что использование таких конструкций перепосылки исключений в общем случае приводит к тому, что исключения теряют свои координаты и становятся совершенно бесполезными при отладке. Мы знаем, что произошла ошибка, даже знаем какая, но только методом научного тыка можем установить где она произошла. Кроме того, в некоторых отладчиках есть полезная функция перехода в режим пошаговой отладки в случае возникновения ошибки… Догадайся, в каком месте произойдёт остановка исполнения т_т
— Тогда надо не делать так и всё будет хорошо! \(^_^)/
— Хех, если бы всё было так просто… К сожалению во всех популярных фреймворках встречаются подобные выкрутасы, причём во многих случаях ошибки просто сглатываются :-\
— Так не используйте их и все дела! =)
— Пхах! Наивный маленький мальчик! Это невозможно… по разным причинам… =(
— Почему? А что ещё остаётся-то? о0'
— Ну, я просто пропатчил используемый нами фреймворк, вычистив все перехваты ошибок нашего приложения, правда это слегка нарушило его api =_=
— Вау, а что вы там такого наворотили-то? 0_0
— Ну вот, лови пример:
  1. each: function(iterator, context) {
  2.     var index = 0;
  3.     try {
  4.         this._each(function(value) {
  5.             iterator.call(context, value, index++);
  6.         });
  7.     } catch (e) {
  8.         if (e != $break) throw e;
  9.     }
  10.     return this;
  11. },
— Что это? +_+'
— Это функция итерирования из prototypeJS. Видно его разработчики совсем не занимались отладкой скриптов в браузерах отличных огнелиса, так как реализовали прерывание цилка именно таким образом. Не буду утомлять тебя яваскриптом — скажу лишь, что я вырезал try-catch, а во все итераторы прописал передачу дополнительного параметра, возвращение которого прерывает итерирование ^_^
— Как всё хорошо закончилось! :-)
— Нет, только началось… Не всегда итерирование можно просто взять и вырезать, ибо иногда требуется выполнить какой-то небезопасный код так, чтобы он не прервал выполнение нашего. Вот, лови пример из jQuery:
  1. // Trigger an inline bound script
  2. try {
  3.     if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
  4.         if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
  5.             event.result = false;
  6.         }
  7.     }
  8. // prevent IE from throwing an error for some elements with some event types, see #3533
  9. } catch (e) {}
— Ой, а как же быть тогда-то? о_о'
— Ну, тут я стащил немного семени Дина Эдвардса ( dean.edwards.name/weblog/2009/03/callbacks-vs-events/ ) и породил враппер, который выполняет некоторый кусок кода безопасно от остального, при этом ни коим образом не перехватывая исключения, а отдавая их на попечение отладчику В-]
— Чего-чего? :-О
— Грубо говоря, последний код можно переписать следующим образом, позвав на помощь моего сына по имени FThread:
  1. // Trigger an inline bound script
  2. // prevent IE from throwing an error for some elements with some event types, see #3533
  3. FThread( function(){
  4.     if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
  5.         if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
  6.             event.result = false;
  7.         }
  8.     }
  9. }).call( this )
— А если не так грубо? :-[
— М… дружок, лучше я оставлю вас на едине — он сам о себе всё красноречиво расскажет ;-)

var FThread= new function(){

Version: 4
Description: 'creates a wrapper for function that allows you to not be afraid of exceptions in'
License: 'public domain'
Implementation:

var scripts= document.getElementsByTagName( 'script' )
var script= scripts[ scripts.length - 1 ]
var starter= document.createElement( 'button' )
starter.id= 'thread starter'
starter.style.display= 'none'
script.parentNode.insertBefore( starter, script )

var FThread= function( proc ){
    var thread= function( ){
        var res, self= this, args= arguments
        starter.onclick= function( ev ){
            ( ev || event ).cancelBubble= true
            starter.onclick= null
            res= thread.proc.apply( self, args )
        }
        starter.click()
        return res
    }
    thread.proc= proc
    return thread
}

Export: return FThread

Usage:

var inverse= FThread(function( a ){
    if( a === -1 ) (void 0)()
    if( a === 0 ) throw Error( 'division by zero' )
    return 1/a
})
alert([ inverse( -1 ), inverse( 0 ), inverse( 1 ) ])
// alerts ",,1" and two exceptions in console log

}
+2
9 марта 2010, 13:41
22

комментарии (36)

0
danilissimus #
>\(^_^)/
>+_+'
>=_=
>В-]
кажется, у вас проблемы с кодировкой.
+1
tenshi #
нет, кажется у вас аллергия на смайлики :-Р
+3
tenshi #
а-а-а! его похитило НЛО! 0_0''
+1
Nc_Soft #
В теме про билайн тоже кого-то похитили, не его ли :D
0
tenshi #
ахтунг! нашествие инопланетян! \(<>..<>)/
+2
remal #
Так о чем статья-то?
+2
tenshi #
это глубокий философский вопрос, разрешение которого сродни становлению новой формы мышления в условиях стагнирующей социальной системы, вырванной из обёртки высоколатентной комуникационной среды
+3
nekt #
Хабросообщество не поймет.

Статья зачотная.
У меня вопрос. А почему используется некий стартер? Зачем? Не проще ли было бы отделать отдельные нити с помощью setTimeout()?
+2
tenshi #
ему же хуже ;-)

setTimeout создаёт асинхронные нити, а FThread — синхронные. это позволяет работать с любыми функциями абсолютно прозрачно
+1
nekt #
А в ие проверялось? Вроде оно у меня когда-то на что-то ругалось при работе с непридомленными кнопками…

Пойду тихонько помечтаю об асинхронном жаваскрипте :)
0
tenshi #
так у меня придомленная кнопка ;-)

хочу эрланг на клиенте х)
+1
nekt #
Да. Невнимательно посмотрел :)

Хотеть не вредно. Вредно не хотеть. Может и дорастет веб до этого. Прожуем — увидим, как говорится.
+1
yroman #
WebWorkers в HTML5, не?
+1
nekt #
Тык да. Но пока они еще в массы проберутся…

/me пошел допиливать скрипты под ие6
+1
Kolyaj #
У WebWorker много ограничений: он должен жить в отдельном файле, файл должен находиться на том же домене, worker не имеет доступа ни к DOM, ни к остальным скриптам, общение с внешним миром только с помощью XMLHttpRequest или postMessage.
+1
yroman #
Дык, а вы хотите кучу геморроя с синхронизацией потоков? Первые два ограничения конечно спорны имхо, но вот ограничение на доступ к DOM, и общение через сообщения это имхо верная стратегия. Как минимум это избавляет от проблем с синхронизацией.
+1
Kolyaj #
Полностью с вами согласен. Я лишь говорю, что WebWorkers предназначены для узкого круга задач.
0
tenshi #
я так понимаю там кооперативные потоки как в setTimeout
0
tenshi #
а из примеров видно что доступ к дому есть
+1
Kolyaj #
www.whatwg.org/specs/web-workers/current-work/

The DOM APIs (Node objects, Document objects, etc) are not available to workers in this version of this specification.
0
tenshi #
м… кажись дошло… ну, наверняка можно сделать мост
0
tenshi #
очень похоже, только некроссбраузерно, требует множить файлы, неудобно использовать и нельзя использовать как обычную функцию
+2
yroman #
Такой вопрос — в приведенном вами коде что за странные дополнения?
Скажем Implementation: Export: — что это и откуда берется? Ни разу не сталкивался с таким оформлением кода.
+1
tenshi #
это метки позволяющие структурировать код %-) в общем случае можно использовать любые свободные имена
+1
ArtyV #
Было бы круто получить dom-независимое решение
+1
tenshi #
ещё бы %-) а те зачем именно независимость от дома?
+1
ArtyV #
Самое главное — для удовлетворения. Ну и IE может не дать приаттачить элемент пока дерево не загрузится.
+1
tenshi #
не может
+1
ArtyV #
Смотря куда будет аттачиться и когда аттач будет производиться, сейчас кейс не вспомню, но суть примерно такова
+1
tenshi #
только если в конец
+1
ArtyV #
Пока что получилось только решение с XHR, как лучше всего проверить синхронность?
0
tenshi #
см ниже >_>
0
tenshi #
ну, если так отрабатывает нормально, то всё пучком: alert([ inverse( -1 ), inverse( 0 ), inverse( 1 ) ])
+1
ArtyV #
Ну нормально ага, у меня правда немного по другому реализован сам вызов, но внутри тупо на .onreadystatechange вешаем нужный колбэк и загружаем тупо #якорь. Проверил: повторно документ не загружает, но надо будет с разными условиями кеширования проверить конечно
0
tenshi #
эм… попробуй какой-нибудь about:blank
а вообще, давай сюда javascript.ru/forum/project/7558-ftread-ispolnenie-funkcijj-v-otdelnykh-potokakh-4.html
+1
ArtyV #
about:blank вызывает секьюрити экспешн во всех браузерах, правда в IE он перед этим срашивает разрешения на выполнение и может даже выполниться : D

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