<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
我們在寫js的時候,經常的會遇到需要非同步去請求介面,或者通過setTimeout或Promise去做什麼事, 然後讓同步程序繼續向下走, 當到某個時間節點的時候或者資料請求成功的時候在通過eventloop
的方式回撥執行。這本身是js的特點和優勢。
但是,非同步佇列執行也存在錯誤的情況,這時,對於怎麼進行錯誤處理,就成了我們的重點。
想一下專案中用到的方式,或者jquery的ajax方式,一般都會有catch、fail之類的回撥方法供我們對錯誤結果進行處理。 那麼現在討論的話題是能不能使用try catch
進行處理。
為什麼寫這篇文章? 是因為我在寫JavaScript 的setTimeout與事件迴圈機制event-loop的時候,舉例express的非同步錯誤獲取的時候,想到了這個點,我覺得有必要單獨拿出來,寫一篇斷篇幅的,又能夠清晰明瞭表達的一篇文章。於是這篇文章便生成了。
好了, 正文開始。
這裡的setTimeout指的是一類,包括 setTimeout
, setInterval
這類所謂宏任務。 他們可以用try catch來捕獲錯誤麼?
try{ setTimeout(() => { let a = c; }, 100) } catch(e) { console.log('能獲取到錯誤麼??', e); }
結果是不能獲取到,程式直接報錯了, 那麼出現的後果可能就是整個頁面掛了,或者在node中,整個服務掛了。 我們的初心是想讓程式更加健壯,但卻做了無用功。
那麼我們在想,既然在setTimeout 外邊無法獲取,那麼能不能在setTimeout裡面先用try catch獲取一下,然後捕獲到錯誤後再傳出去呢? 想到就幹,繼續分析:
try{ setTimeout(() => { try { let a = c; } catch(e) { throw new Error('some variable is not defined'); } }, 100) } catch(e) { console.log('能獲取到錯誤麼??', e); }
很抱歉,想法很好,但是也不行。外邊也catch不到
。
好了,我們把這個疑問分析一下吧。其實,這裡的根本原因還是剛開始提到的事件迴圈
。 事件迴圈不是空空的一句表述、一個概念,而是在程式碼中實實在在存在的。
具體事件迴圈的相關知識,可以看下我很早前寫的JavaScript 的setTimeout與事件迴圈機制event-loop 文章。
回到這個例子中, 最外層的try catch是在一個task
中,我們定義它為我們js檔案的同步主任務,從上到下執行到這裡了, 然後,會把裡面的setTimeout推到一個任務佇列
中, 這個佇列是儲存在記憶體中的,由V8來管理。然後主task就繼續向下執行, 一直到結束。
當該setTimeout時間到了,且沒有其它的task執行了, 那麼,就將這個setTimeout的程式碼推入執行棧
開始執行。 當執行到錯誤程式碼的時候,也就是這個 let a = c
, 因為c未定義,所以就會報錯。
但問題的本質是,這個錯誤跟最外層的try catch並不在一個執行棧中,當裡面執行的時候,外邊的這個task早已執行完, 他們的context(上下文)已經完全不同了。
所以,頁面會直接報錯,甚至程式崩潰。
我們知道,Promise
也是一個非同步的處理過程,它對應事件迴圈中的微任務
。 那麼這裡其實與上面的setTimeout存在同樣的問題。
舉個例子:
try { new Promise((resolve, reject) => { reject('promise error'); }) } catch(e) { console.log('非同步錯誤,能catch到麼??', e); }
相信大家能夠推匯出結果了: 也catch不到
原因其實與上面的setTimeout是一樣的,執行棧上下文已經不同了。
那麼針對Promise,ECMA官方已經給我們提供了一個方法,那就是 catch
, 通過catch我們獲取到錯誤,可以阻止程式崩潰。 但是喜歡發散思維的你可能會想到, 那我用catch接到了,是不是就可以讓外層的catch獲取到了呢? 想到就試一下
try { new Promise((resolve, reject) => { reject('promise error'); }).catch(e => { throw new Error(e); }) } catch(e) { console.log('非同步錯誤,能catch到麼??', e); }
結果就是 不行
。相信大家通過我詳細的例子和思維脈絡,對這塊已經真正掌握了吧?
那麼通過上面的,大家可能會有一種想法,只要是callback,就是catch不住的。 其實這種想法是錯誤的,我通過一個例子來證明。
function Fn(cb) { console.log('callback執行了'); cb(); } try { const cb = () => { throw new Error('callback執行錯誤'); } Fn(cb); } catch(e) { console.log('能夠catch住麼???') }
其實這裡就是個煙霧彈, 考驗大家對這個事件迴圈相關機制是不是明白了。
現在的專案中,大家越來越願意使用Async await
這對 es7標準裡的api了。 因為它們這對組合是在是太好用了。 那麼通過非同步等待的方式,用try catch能夠行麼?
那麼咱們使用一個例子驗證一下:
const asyncFn = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject('asyncFn執行時出現錯誤了') }, 100); }) } const executedFn = async () => { try{ await asyncFn(); }catch(e) { console.log('攔截到錯誤..', e); } }
如果執行一下,就發現: catch到了!
asyncFn
裡面是有 Promise的,為什麼外邊就能catch到了呢? 是不是跟上面講的矛盾了呢? 其實並沒有。 看我分析一下:
async-await 是使用生成器、promise 和協程實現的,wait操作符還儲存返回事件迴圈之前的執行上下文,以便允許promise操作繼續進行。當內部通知解決等待的承諾時,它會在繼續之前恢復執行上下文。
所以說,能夠回到最外層的上下文, 那就可以用try catch 啦。
到此這篇關於JavaScript非同步佇列進行try catch時的問題解決的文章就介紹到這了,更多相關JS try catch內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45