console.log 是異步輸出還是同步

有時候調(diào)試代碼,發(fā)現(xiàn)所看的結(jié)果與期望的有差異,誤導了我們的判斷,找錯了方向,耽誤了很多時間,console.log()的輸出竟然會出現(xiàn)異步輸出的情況,因而所以這里記錄一下遇到的這個問題,加深印象。

chrome 瀏覽器測試

image

可以看出,當 console.log(obj.per) 看到的還是未修改的 vv,一旦展開卻變成了 呱呱,為什么會有這個異常輸出

原因:

這里不得不提到 js 的對象是引用類型,每次使用對象時候,都只是引用了對象在堆中的引用,當修改了 obj.per.name 時候,也修改了堆中引用的 name,當 console.log(obj.per)

打印的是對象當時的快照信息,當展開對象時候,會去內(nèi)存讀對象的屬性值。

為什么開發(fā)者工具有這個表現(xiàn)?

《你不知道的javascript中卷》第二部分異步和性能1.1節(jié)異步控制臺部分有提及:

翻譯:并沒有什么規(guī)范或一組需求指定console.* 方法族如何工作——它們并不是JavaScript 正式的一部分,而是由宿主環(huán)境(請參考本書的“類型和語法”部分)添加到JavaScript 中的。因此,不同的瀏覽器和JavaScript 環(huán)境可以按照自己的意愿來實現(xiàn),有時候這會引起混淆。

尤其要提出的是,在某些條件下,某些瀏覽器的console.log(..) 并不會把傳入的內(nèi)容立即輸出。出現(xiàn)這種情況的主要原因是,在許多程序(不只是JavaScript)中,I/O 是非常低速的阻塞部分。所以,(從頁面/UI 的角度來說)瀏覽器在后臺異步處理控制臺I/O 能夠提高性能,這時用戶甚至可能根本意識不到其發(fā)生。

書中還舉了一個例子

var a = {    index: 1};// 然后console.log( a ); // ??// 再然后a.index++;

類似的,當執(zhí)行輸出 a 時,會顯示 a 的快照,而 a.index ++ 的確嚴格執(zhí)行在 console.log 之后,但當你展開 對象 a 時候,會去內(nèi)存中去讀取 a.index 值,瀏覽器可能會認為需要把控制臺I/O 延遲到后臺,這時候可能修改成了 2。

到底什么時候控制臺I/O 會延遲,甚至是否能夠被觀察到,這都是游移不定的。

所以如果在調(diào)試的過程中遇到對象在console.log(..) 語句之后被修改,可你卻看到了意料之外的結(jié)果,要意識到這可能是這種I/O 的異步化造成的。

書中建議:

如果遇到這種少見的情況,最好的選擇是在JavaScript 調(diào)試器中使用斷點,而不要依賴控制臺輸出。次優(yōu)的方案是把對象序列化到一個字符串中,以強制執(zhí)行一次“快照”,比如通過JSON.stringify(..)。

結(jié)論:

由此可見,console.log打印出來的內(nèi)容并不是一定百分百可信的內(nèi)容。一般對于基本類型number、string、boolean、null、undefined的輸出是可信的。但對于Object等引用類型來說,則就會出現(xiàn)上述異常打印輸出。所以對于一般基本類型的調(diào)試,調(diào)試時使用console.log來輸出內(nèi)容時,不會存在坑。但調(diào)試對象時,最好還是使用打斷點(debugger)這樣的方式來調(diào)試更好。

文獻來源:https://github.com/Mmzer/think/issues/30

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容