摘要:對于引擎來說,兩者獲取堆棧的方式是不同的。對于引擎來說,堆棧信息附加在了函數所返回的并在鏈中傳遞,這樣函數也能在需要的時候獲取堆棧信息。使用可以實時監控線上應用的錯誤,并獲取完整的堆棧信息。
譯者按: Async/Await真的只是簡單的語法糖嗎?No!
原文:Asynchronous stack traces: why await beats .then()
作者: Mathias Bynens: Google V8引擎開發者
譯者:Fundebug
為了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原作者所有,翻譯僅用于學習。
與直接使用Promise相比,使用Async/Await不僅可以提高代碼的可讀性,同時也可以優化JavaScript引擎的執行方式。這篇博客將介紹Async/Await是如何優化JavaScript引擎對堆棧信息的處理。
Async/Await與Promise最大區別在于:await b()會暫停所在的async函數的執行;而Promise.then(b)將b函數加入回調鏈中之后,會繼續執行當前函數。對于堆棧來說,這個不同點非常關鍵。
當一個Promise鏈拋出一個未處理的錯誤時,無論我們使用await b()還是Promise.then(b),JavaScript引擎都需要打印錯誤信息及其堆棧。對于JavaScript引擎來說,兩者獲取堆棧的方式是不同的。
Promise.then(b)示例代碼中,函數c()會在異步函數b()成功resolve之后執行:
const a = () => { b().then(() => c()); };
當調用a()函數時,這些事情同步發生:
b()函數被調用,它會返回一個Promise,這個Promise會在未來的某個時刻resolve。
.then()回調函數(實際調用了c()函數)被添加到回調鏈。
這樣,a()函數內的代碼就執行完了。a()函數不會被暫停,因此在異步函數b()resolve時,a()函數的作用域已經不存在了。假設b()或者c()拋出了一個錯誤,則堆棧信息中應該包含a()函數,因為它們都是在a()函數內被調用。對a()函數的任何引用都不存在了,要如何生成包含a()的堆棧信息呢?
為了解決這個問題,JavaScript引擎需要做一些額外的工作:它會及時記錄并且保存堆棧信息。對于V8引擎來說,堆棧信息附加在了b()函數所返回的Promise并在Promise鏈中傳遞,這樣c()函數也能在需要的時候獲取堆棧信息。
記錄堆棧信息需要時間,這樣會降低性能;而保存堆棧信息需要占用額外的內存。
使用Fundebug, 可以實時監控線上應用的錯誤,并獲取完整的堆棧信息。
Await b()我們可以使用Async/Await實現同樣的代碼,同步函數c()會等到異步函數b()執行結束之后再執行:
const a = async () => { await b(); c(); };
使用await時,無需存儲當前的堆棧信息,因為存儲b()到a()的指針就足夠了。當等待b()函數執行時,a()函數被暫停了,因此a()函數的作用域還在內存可以訪問。如果b()函數拋出一個錯誤,堆棧信息可以通過指針迅速生成。如果c()函數拋出一個錯誤,堆棧信息也可以像同步函數一樣生成,因為c()函數是在a()函數中執行的。不論是b()還是c(),我們都不需要去存儲堆棧信息,因為堆棧信息可以在需要的時候立即生成。而存儲指針,顯然比存儲堆棧更加節省內存。
建議很多ECMAScript語法特性看起來都只是語法糖,其實并非如此,至少Async/Await絕不僅僅只是語法糖。
為了讓JavaScript引擎處理堆棧的方式性能更高并且更加節省內存,請遵循這些建議:
使用Async/Await,而不是直接使用Promise
使用babel-preset-env,避免Babel不必要地轉換Async/Await
盡管V8引擎還沒有實現這些優化,請遵循這些建議。當我們為V8實現這些優化的時候,你的程序可以保證最佳的性能。(作者是V8引擎的開發者)
一般來說,盡量不要去使用Babel轉碼器。所有支持Service Workers的瀏覽器都支持Async/Await,因此沒有必要去對Async/Await轉碼。這一點對于JavaScript modules via script tag同樣適用。關于這一點,大家可以參考Deploying ES2015+ Code in Production Today。
關于FundebugFundebug專注于JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了6億+錯誤事件,得到了Google、360、金山軟件等眾多知名用戶的認可。歡迎免費試用!
版權聲明轉載時請注明作者Fundebug以及本文地址:
https://blog.fundebug.com/2018/07/18/javascript-engine-await-promise/
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96706.html
摘要:事件循環從回調隊列中獲取并將其推入調用堆棧。執行從調用堆棧中移除從調用堆棧中移除快速回顧值得注意的是,指定了事件循環應該如何工作,這意味著在技術上它屬于引擎的職責范圍,不再僅僅扮演宿主環境的角色。 此篇是 JavaScript是如何工作的第四篇,其它三篇可以看這里: JavaScript是如何工作的:引擎,運行時和調用堆棧的概述! JavaScript是如何工作的:深入V8引擎&編寫...
摘要:函數會在之后的某個時刻觸發事件定時器。事件循環中的這樣一次遍歷被稱為一個。執行完畢并出棧。當定時器過期,宿主環境會把回調函數添加至事件循環隊列中,然后,在未來的某個取出并執行該事件。 原文請查閱這里,略有改動。 本系列持續更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第四章。 現在,我們將會通過回顧單線程環境下編程的弊端及如何克服這些困難以創建令人驚嘆...
摘要:拋出的錯誤對象會被方法回調函數接收到命令命令后面是一個對象,返回該對象的結果。有人將其稱之為宏任務微任務,定時器就屬于宏任務的范疇。 前言 上一篇 前端面試題-JavaScript(一), 感興趣的小伙伴也可以移步這里查看 完整版JavaScript面試題,面試題會不定期更新加進去一些個人工作中遇到的或者認為比較重要的東西,后面會涉及到前端的各個方面,感興趣的小伙伴可以關注哦! 如果文...
摘要:由于是單線程的,這些方法就會按順序被排列在一個單獨的地方,這個地方就是所謂執行棧。事件隊列每次僅執行一個任務,在該任務執行完畢之后,再執行下一個任務。 Event Loop 是 JavaScript 異步編程的核心思想,也是前端進階必須跨越的一關。同時,它又是面試的必考點,特別是在 Promise 出現之后,各種各樣的面試題層出不窮,花樣百出。這篇文章從現實生活中的例子入手,讓你徹底理解 E...
閱讀 793·2021-10-09 09:44
閱讀 700·2019-08-30 13:55
閱讀 3157·2019-08-29 15:07
閱讀 3224·2019-08-29 13:09
閱讀 2416·2019-08-29 11:10
閱讀 1293·2019-08-26 14:05
閱讀 3598·2019-08-26 13:57
閱讀 2209·2019-08-23 16:42