Beim Programmieren erhalten wir oft korrekte Geschäftsbeschreibungsdokumente oder Spezifikationen für Geschäftsprozesse, aber die tatsächliche Entwicklung ist voller Dornen und Ausnahmen, und diese Ausnahmen umfassen auch Ausnahmen für geschäftliche Anwendungsfälle enthalten. Bei Ausnahmen für geschäftliche Anwendungsfälle haben wir keine andere Wahl, als von Implementierern und Benutzern die Zusammenarbeit zu verlangen, um vernünftige Lösungen bereitzustellen, während technische Ausnahmen von uns Programmierern behandelt werden müssen, und das möchte ich festhalten.
Ich habe vor, es in zwei Artikel zu unterteilen: „Front-End Magic Hall – Ausnahmen sind nicht nur Try/Catch“ und „Front-End Magic Hall – Call Stack, der Schatz in Ausnahmefällen“. In/benutzerdefinierte Ausnahmeklassen und das Erfassen von Laufzeitausnahmen/Syntaxausnahmen/Netzwerkanforderungsausnahmen/PromiseRejection-Ereignissen, was ist ein Aufrufstapel und wie erhält man Aufrufstapel-bezogene Informationen?
Freust du dich schon bevor es losgeht? Okay, alle zusammen, haltet euch an den Handläufen fest, der alte Fahrer fährt gleich^_^
In diesem Artikel wird der folgende Inhalt beschrieben:
Abnormalität oder kein Fehler? Welche Auswirkungen wird es auf unseren Code haben?
Welche integrierten Ausnahmetypen gibt es?
Lassen Sie uns Ihren eigenen Ausnahmetyp schreiben!
Um „Laufzeitausnahmen“ in „synchronem Code“ zu erfassen, reicht die Verwendung von try/catch
aus.
Der „universelle“ Ausnahmefänger window.onerror
, ist er wirklich allmächtig?
Promise.reject löst auch eine Ausnahme aus. Was soll ich tun?
404 und andere Netzwerkanfrageausnahmen, möchten Sie wirklich später aufwachen?
Beim Erlernen von Java wird uns mitgeteilt, dass Ausnahmen (Exception) und Fehler (Error) unterschiedlich sind. Ausnahmen führen nicht zum Abbruch des Prozesses und können repariert (try/catch) werden, Fehler verursachen jedoch Der Prozess wurde abgebrochen und kann daher nicht repariert werden. Wenn es um JavaScript geht, müssen wir uns nur mit Ausnahmen auseinandersetzen (obwohl der Name der Ausnahmeklasse „Error“ lautet oder das Wort „Error“ enthält). Das Auftreten von Ausnahmen führt höchstens zum Absturz der aktuell ausgeführten Engine Aufgabe.
Oben wurde erwähnt, dass die häufigste Ausnahme die Beendigung der aktuell ausgeführten Aufgabe ist. Was bedeutet das? Dies beinhaltet das Prinzip der Ereignisschleife. Lassen Sie mich versuchen, es grob mit Code zu erklären.
<script> // 1.当前代码块将作为一个任务压入任务队列中,JavaScript线程会不断地从任务队列中提取任务执行; // 2.当任务执行过程中报异常,且异常没有捕获处理,则会一路沿着调用栈从顶到底抛出,最终终止当前任务的执行; // 3.JavaScript线程会继续从任务队列中提取下一个任务继续执行。 function a(){throw Error("test")} function b(){a()} b() console.log("永远不会执行!") </script> <script> // 下一个任务 console.log("你有你抛异常,我照样执行!") </script>
Apropos integrierte Ausnahmeklasse: Als Erstes muss der Vorfahrentyp Error
erwähnt werden. Alle anderen integrierten Ausnahmeklassen und benutzerdefinierten Klassen müssen ihn erben. Seine Standardattribute und -methoden sind lediglich die folgenden:
@prop {String} name - 异常名称 @prop {String} message - 供人类阅读的异常信息 @prop {Function} constructor - 类型构造器 @method toString():String - 输出异常信息
Da es zu wenige Standardattribute gibt, kann es den Entwicklern keine effektiveren Informationen zur Lokalisierung der Anomalie und zur Reproduktion des Unfallorts liefern. Jeder Browserhersteller fügte viele Attribute hinzu und wurde dann nach und nach zum De-facto-Standard.
@prop {String} fileName - 异常发生的脚本URI @prop {number} lineNumber - 异常发生的行号 @prop {number} columnNumber - 异常发生的列号 @prop {String} stack - 异常发生时的调用栈信息,IE10及以上才支持 @method toSource():String - 异常发生的脚本内容
Zusätzlich hat Juhard auch die folgenden zwei Attribute hinzugefügt
@prop {String} description - 和message差不多 @prop {number} number - 异常类型的编号,巨硬为每个异常设置了一个唯一的编号
Jetzt möchte ich ein Fehlerobjekt instanziieren, rufe einfach Error()
oder new Error()
auf Wenn Sie gleichzeitig eine Nachricht festlegen möchten, ändern Sie sie in Error("test")
oder new Error("test")
. Tatsächlich sieht die Konstruktorsignatur von Error so aus:
@constructor @param {String=} message - 设置message属性 @param {String=} fileName - 设置fileName属性 @param {number=} lineNumber - 设置lineNUmber属性
Werfen wir nun einen Blick auf die spezifischen integrierten Ausnahmetypen!
EvalError, eine Ausnahme, die beim Aufruf von eval()
auftritt, ist veraltet und wird nur aus Gründen der Abwärtskompatibilität
InternalError, interne Ausnahme der JavaScript-Engine, einzigartig bereitgestellt von FireFox!
RangeError tritt auf, wenn der tatsächliche Parameter der Funktion außerhalb der Grenzen liegt, beispielsweise wenn der Eingabeparameter in , Array
, < unzulässig ist 🎜> und Number.toExponential
. Number.toFixed
Number.toPrecision
, tritt auf, wenn auf eine nicht deklarierte Variable verwiesen wird
, wenn eine Syntax analysiert wird Fehler
tritt auf. Wenn der Wert nicht dem erwarteten Typ entspricht, meldet auch diesen Fehler null.f()
tritt auf, wenn ein ungültiger URI an die globale URI-Handler-Funktion übergeben wird, z. B. , also , decodeURIComponent('%')
, decodeURIComponent
, decodeURI
encodeURIComponent
encodeURI
function MyError(message, fileName, lineNumber){ if (this instanceof MyError);else return new MyError(message, fileName, lineNumber) this.message = message || "" if (fileName){ this.fileName = fileName } if (lineNumber){ this.lineNumber = lineNumber } } var proto = MyError.prototype = Object.create(Error.prototype) proto.name = "MyError" proto.constructor = MyError
(defn ^export MyError [& args] (this-as this (if (instance? MyError this) (let [ps ["message" "fileName" "lineNumber"] idxs (-> (min (count args) (count ps)) range)] (reduce (fn [accu i] (aset accu (nth ps i) (nth args i)) accu) this idxs)) (apply new MyError args)))) (def proto (aset MyError "prototype" (.create js/Object (.-prototype Error)))) (aset proto "name" "MyError") (aset proto "constructor" MyError)
try/catch
Um das Risiko zu vermeiden, dass normaler Code aufgrund des Auftretens von Ausnahmen übersprungen wird, sind wir es gewohnt try/catch
try{ throw Error("unexpected operation happen...") } catch (e){ console.log(e.message) }
(try (throw (Error. "unexpected operation happen...") (catch e (println (.-message e)))))
很多时我们会以为这样书写就万事大吉了,但其实try/catch
能且仅能捕获“同步代码”中的"运行时异常"。
1."同步代码"就是说无法获取如setTimeout
、Promise
等异步代码的异常,也就是说try/catch
仅能捕获当前任务的异常,setTimeout
等异步代码是在下一个EventLoop中执行。
// 真心捕获不到啊亲~! try{ setTimeout(function(){ throw Error("unexpected operation happen...") }, 0) } catch(e){ console.log(e) }
2."运行时异常"是指非SyntaxError,也就是语法错误是无法捕获的,因为在解析JavaScript源码时就报错了,还怎么捕获呢~~
// 非法标识符a->b,真心捕获不到啊亲~! try{ a->b = 1 } catch(e){ console.log(e) }
这时大家会急不可待地问:“异步代码的异常咋办呢?语法异常咋办呢?”在解答上述疑问前,我们先偏离一下,稍微挖挖throw
语句的特性。
throw
后面可以跟什么啊? 一般而言我们会throw
一个Error或其子类的实例(如throw Error()
),其实我们throw
任何类型的数据(如throw 1
,throw "test"
,throw true
等)。但即使可以抛出任意类型的数据,我们还是要坚持抛出Error或其子类的实例。这是为什么呢?
try{ throw "unexpected operation happen..." } catch(e){ console.log(e) } try{ throw TypeError("unexpected operation happen...") } catch(e){ if ("TypeError" == e.name){ // Do something1 } else if ("RangeError" == e.name){ // Do something2 } }
原因显然易见——异常发生时提供信息越全越好,更容易追踪定位重现问题嘛!
window.onerror
,真的万能吗? 在每个可能发生异常的地方都写上try/catch
显然是不实际的(另外还存在性能问题),即使是罗嗦如Java我们开发时也就是不断声明throws
,然后在顶层处理异常罢了。那么,JavaScript中对应的顶层异常处理入口又在哪呢?木有错,就是在window.onerror
。看看方法签名吧
@description window.onerror处理函数 @param {string} message - 异常信息" @param {string} source - 发生异常的脚本的URI @param {number} lineno - 发生异常的脚本行号 @param {number} colno - 发生异常的脚本列号 @param {?Error} error - Error实例,Safari和IE10中没有这个实参
这时我们就可以通过它捕获除了try/catch
能捕获的异常外,还可以捕获setTimeout
等的异步代码异常,语法错误。
window.onerror = function(message, source, lineno, colno, error){ // Do something you like. } setTimeout(function(){ throw Error("oh no!") }, 0) a->b = 1
这样就满足了吗?还没出大杀技呢——屏蔽异常、屏蔽、屏~~
只有onerror函数返回true
时,异常就不会继续向上抛(否则继续上抛就成了Uncaught Error了)。
// 有异常没问题啊,因为我看不到^_^ window.onerror = function(){return true}
现在回到标题的疑问中,有了onerror就可以捕获所有异常了吗?答案又是否定的(我的娘啊,还要折腾多久啊~0~)
Chrome中对于跨域脚本所报的异常,虽然onerror能够捕获,但统一报Script Error
。若要得到正确的错误信息,则要配置跨域资源共享CORS才可以。
window.onerror
实际上采用的事件冒泡的机制捕获异常,并且在冒泡(bubble)阶段时才触发,因此像网络请求异常这些不会冒泡的异常是无法捕获的。
Promise.reject产生的未被catch的异常,window.onerror
也是无能为力。
通过Promise来处理复杂的异步流程控制让我们得心应手,但倘若其中出现异常或Promise实例状态变为rejected时,会是怎样一个状况,我们又可以如何处理呢?
Promise实例的初始化状态是pending,而发生异常时则为rejected,而导致状态从pending转变为rejected的操作有
调用Promise.reject
类方法
在工厂方法中调用reject
方法
在工厂方法或then回调函数中抛异常
// 方式1 Promise.reject("anything you want") // 方式2 new Promise(function(resolve, reject) { reject("anything you want") }) // 方式3 new Promise(function{ throw "anything you want" }) new Promise(function(r) { r(Error("anything you want" ) }).then(function(e) { throw e })
当Promise实例从pending转变为rejected时,和之前谈论到异常一样,要么被捕获处理,要么继续抛出直到成为Uncaught(in promise) Error
为止。
catch
掉 若在异常发生前我们已经调用catch
方法来捕获异常,那么则相安无事
new Promise(function(resolve, reject){ setTimeout(reject, 0) }).catch(function(e){ console.log("catch") return "bingo" }).then(function(x){ console.log(x) }) // 回显 bingo
若在异常发生前我们没有调用catch
方法来捕获异常,还是可以通过window
的unhandledrejection
事件捕获异常的
window.addEventListener("unhandledrejection", function(e){ // Event新增属性 // @prop {Promise} promise - 状态为rejected的Promise实例 // @prop {String|Object} reason - 异常信息或rejected的内容 // 会阻止异常继续抛出,不让Uncaught(in promise) Error产生 e.preventDefault() })
catch
由于Promise实例可异步订阅其状态变化,也就是可以异步注册catch处理函数,这时其实已经抛出Uncaught(in promise) Error
,但我们依然可以处理
var p = new Promise(function(resolve, reject){ setTimeout(reject, 0) }) setTimeout(function(){ p.catch(function(e){ console.log("catch") return "bingo" }) }, 1000)
另外,还可以通过window
的rejectionhandled
事件监听异步注册catch处理函数的行为
window.addEventListener("rejectionhandled", function(e){ // Event新增属性 // @prop {Promise} promise - 状态为rejected的Promise实例 // @prop {String|Object} reason - 异常信息或rejected的内容 // Uncaught(in promise) Error已经抛出,所以这句毫无意义^_^ e.preventDefault() })
注意:只有抛出Uncaught(in promise) Error
后,异步catch才会触发该事件。
也许我们都遇到<img src="./404.png">
报404网络请求异常的情况,然后测试或用户保障怎么哪个哪个图标没有显示。其实我们我们可以通过以下方式捕获这类异常
window.addEventListener("error", function(e){ // Do something console.log(e.bubbles) // 回显false }, true)
由于网络请求异常不会冒泡,因此必须在capture阶段捕获才可以。但还有一个问题是这种方式无法精确判断异常的HTTP状态是404还是500等,因此还是要配合服务端日志来排查分析才可以。
Das obige ist der detaillierte Inhalt vonFragen zum Front-End-Ausnahme-Try/Catch. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!