詳解JavaScript Alert函數(shù)執(zhí)行順序問(wèn)題
問(wèn)題
前幾天使用 JavaScript 寫(xiě) HTML 頁(yè)面時(shí)遇到了一個(gè)奇怪的問(wèn)題:
我想實(shí)現(xiàn)的功能是通過(guò) confirm() 彈窗讓用戶(hù)選擇不同的需求,每次選擇后都將選擇結(jié)果暫時(shí)輸出到頁(yè)面上,最后一次選擇結(jié)束后再一次性將選項(xiàng)傳到后端處理。 代碼類(lèi)似于:
var step1 = confirm("exec step1?"); $('#result').html($('#result').html() + "\n" + step1); var step2 = confirm("exec step2?"); $('#result').html($('#result').html() + "\n" + step2); var step3 = confirm("exec step3?"); $('#result').html($('#result').html() + "\n" + step3); send(step1, step2, step3);
可是代碼運(yùn)行后卻發(fā)現(xiàn):每次在執(zhí)行完 confirm 函數(shù),用戶(hù)選擇選項(xiàng)之后,頁(yè)面并沒(méi)有刷新,step1, step2 的結(jié)果沒(méi)有實(shí)時(shí)刷新到頁(yè)面上,而是到最后一步跟 step3 一塊顯示了出來(lái)。
后續(xù)嘗試了 alert() 和 prompt() 這兩個(gè)跟 confirm 類(lèi)似的彈對(duì)話(huà)框函數(shù),情況都與此相同,它們都會(huì)跳過(guò)頁(yè)面渲染先被執(zhí)行。
此時(shí),還有更詭異的情況,我們給某一個(gè) div 里賦值后,立刻 alert 此 div 里的內(nèi)容,會(huì)發(fā)現(xiàn) alert 顯示正確的內(nèi)容,而 div 里的內(nèi)容卻沒(méi)有更新,并且會(huì)一直阻塞到我們點(diǎn)擊確定。
如圖:
alert、prompt、confirm 三個(gè)函數(shù)都類(lèi)似,接下來(lái)我們就用最簡(jiǎn)單的 alert 來(lái)說(shuō)。
分析
解決這個(gè)問(wèn)題之前先了解一下它是怎么導(dǎo)致的,而要了解它需要從 JavaScript 的線(xiàn)程模型說(shuō)起。
JavaScript 引擎是單線(xiàn)程運(yùn)行的,瀏覽器無(wú)論在什么時(shí)候都只且只有一個(gè)線(xiàn)程在運(yùn)行 JavaScript 程序,初衷是為了減少 DOM 等共享資源的沖突??墒菃尉€(xiàn)程永遠(yuǎn)會(huì)面臨著一個(gè)問(wèn)題,那就是某一段代碼阻塞會(huì)導(dǎo)致后續(xù)所有的任務(wù)都延遲。又由于 JavaScript 經(jīng)常需要操作頁(yè)面 DOM 和發(fā)送 HTTP 請(qǐng)求,這些 I/O 操作耗時(shí)一般都比較長(zhǎng),一旦阻塞,就會(huì)給用戶(hù)非常差的使用體驗(yàn)。
于是便有了事件循環(huán)(event loop)的產(chǎn)生,JavaScript 將一些異步操作或 有I/O 阻塞的操作全都放到一個(gè)事件隊(duì)列,先順序執(zhí)行同步 CPU代碼,等到 JavaScript 引擎沒(méi)有同步代碼,CPU 空閑下來(lái)再讀取事件隊(duì)列的異步事件來(lái)依次執(zhí)行。
這些事件包括:
- setTimeout() 設(shè)置的異步延遲事件;
- DOM 操作相關(guān)如布局和繪制事件;
- 網(wǎng)絡(luò) I/O 如 AJAX 請(qǐng)求事件;
- 用戶(hù)操作事件,如鼠標(biāo)點(diǎn)擊、鍵盤(pán)敲擊。
解決
明白了原理, 再解決這個(gè)問(wèn)題就有了方向,我們來(lái)分析這個(gè)問(wèn)題:
1.由于頁(yè)面渲染是 DOM 操作,會(huì)被 JavaScript 引擎放入事件隊(duì)列;
2.alert() 是 window 的內(nèi)置函數(shù),被認(rèn)為是同步 CPU代碼;
3.JavaScript 引擎會(huì)優(yōu)先執(zhí)行同步代碼,alert 彈窗先出現(xiàn);
4.alert 有特殊的阻塞性質(zhì),JavaScript 引擎的執(zhí)行被阻塞??;
5.點(diǎn)擊 alert 的“確定”,JavaScript 沒(méi)有了阻塞,執(zhí)行完同步代碼后,又讀取事件隊(duì)列里的 DOM 操作,頁(yè)面渲染完成。
由上述原因,導(dǎo)致了詭異的 “Alert執(zhí)行順序問(wèn)題”。 我們無(wú)法將頁(yè)面渲染變成同步操作,那么只好把 alert() 變?yōu)楫惒酱a,從而才能在頁(yè)面渲染之后執(zhí)行。
對(duì)于這個(gè)解決方向,我們有兩種方法可以使用:
替換 Alert() 函數(shù)
首先我們考慮替換掉 alert 函數(shù)的功能。其實(shí)大多數(shù)情況下我們替換掉 alert 并不是它不符合我們期待的執(zhí)行順序,而是因?yàn)樗鼘?shí)在是太丑了,而且也不支持各種美化,可以想像在一個(gè)某一特定主題的網(wǎng)站上忽然彈出來(lái)一個(gè)灰色單調(diào)的對(duì)話(huà)框是多么不和諧。
這個(gè)我們可以考慮 Bootstrap 的 modal 模塊,Bootstrap 在絕大多數(shù)網(wǎng)站上都在應(yīng)用,而多引入一個(gè) modal 模塊也不會(huì)有多大影響。我們使用 modal 構(gòu)造一個(gè)彈出對(duì)話(huà)框的樣子,使用 modal 的 modal('toggle')/modal('show')/modal('hide') 方法可以很方便地控制 modal 的顯隱。
替換掉對(duì)話(huà)框后,我們還需要解決后續(xù)代碼執(zhí)行的問(wèn)題。使用 alert 函數(shù)時(shí),我們點(diǎn)擊確定后代碼還會(huì)繼續(xù)執(zhí)行,而使用我們自定義的對(duì)話(huà)框可沒(méi)有這種功能了,需要考慮把后續(xù)代碼綁定在對(duì)話(huà)框的點(diǎn)擊按鈕上,這就需要使用 DOM 的 onclick
屬性了,我們將后續(xù)函數(shù)內(nèi)容抽出一個(gè)新的函數(shù),在彈出對(duì)話(huà)框后將這個(gè)函數(shù)綁定在按鈕的 onclick 事件上即可。
這里還需要注意,新函數(shù)內(nèi)應(yīng)該包括關(guān)閉 modal 對(duì)話(huà)框的內(nèi)容。
當(dāng)然,我們還可以再優(yōu)化一下,抽象出來(lái)一個(gè)用來(lái)彈出對(duì)話(huà)框的函數(shù)替代 alert 函數(shù),示例如下:
window.alert = function (message, callbackFunc) { $('#alertContent').html(message); $('#modal').show(); $('#confirmButton').onclick(function () { $('#modal').hide(); callbackFunc(); }); };
如此,我們?cè)谛枰獜棾隹驎r(shí)調(diào)用新的 alert 函數(shù),并傳入 callbackFunc ,在里面做后續(xù)的事情就可以了。
setTimeOut函數(shù)
當(dāng)然,并不是所有人都愿意使用新的對(duì)話(huà)框替換 alert 函數(shù)的對(duì)話(huà)框,總感覺(jué)上面的方法不是特別的優(yōu)雅,對(duì)此,我們可以采用另外的方法解決這個(gè)問(wèn)題。
前端的同學(xué)應(yīng)該對(duì) setTimeout() 這個(gè)函數(shù)不陌生,使用它,可以延遲執(zhí)行某些代碼。而對(duì)于延遲執(zhí)行的代碼,JavaScript 引擎總是把這些代碼放到事件隊(duì)列里去,再去檢查是否已經(jīng)到了執(zhí)行時(shí)間,再適時(shí)執(zhí)行。代碼進(jìn)入事件隊(duì)列,就意味著代碼變成和頁(yè)面渲染事件一樣異步了。由于事件隊(duì)列是有序的,我們?nèi)绻?setTimeout 延時(shí)執(zhí)行,就可以實(shí)現(xiàn)在頁(yè)面渲染之后執(zhí)行 alert 的功能了。
setTimeout 的函數(shù)原型為 setTimeout(code, msec),code 是要變?yōu)楫惒降拇a或函數(shù),msec 是要延時(shí)的時(shí)間,單位為毫秒。這里我們不需要它延時(shí),只需要它變?yōu)楫惒骄托辛?,所以可以?msec 設(shè)置為 0;
同樣,alert 之后的代碼我們也需要處理,將它們跟 alert 一塊放到 setTimeout 里異步執(zhí)行。這樣,代碼就變?yōu)?setTimeout("alert('msg');doSomething();", 0);,如果覺(jué)得代碼不夠美觀或字符串不好處理的話(huà),可以將后續(xù)代碼封裝成一個(gè)函數(shù)放到 doSomething() 里即可。
小結(jié)
在上面的兩個(gè)解決方案中,都利用了 JavaScript 的回調(diào)函數(shù),前者將函數(shù)所為 alert 的參數(shù)并綁定到 DOM 的 onclick 事件,后者使用 setTimeout 將函數(shù)轉(zhuǎn)為異步執(zhí)行。JavaScript 的回調(diào)函數(shù)確實(shí)非常強(qiáng)大,使用起來(lái)也很簡(jiǎn)單,但是卻有一個(gè)隱含的問(wèn)題,就是回調(diào)嵌套問(wèn)題,單層的回調(diào)很容易理解,但如果要實(shí)現(xiàn)像我的需求一樣,有多個(gè) alert 和頁(yè)面渲染輪流執(zhí)行的情況,需要面臨的可能就是“回調(diào)地獄”, onclick 事件綁定里的函數(shù)又要嵌套綁定 onclick 函數(shù), setTimeout 里還需要另一個(gè) setTimeout 語(yǔ)句,一旦出現(xiàn)問(wèn)題,排查起來(lái)就比較麻煩了。
以上就是詳解JavaScript Alert函數(shù)執(zhí)行順序問(wèn)題的詳細(xì)內(nèi)容,更多關(guān)于JavaScript Alert函數(shù)執(zhí)行順序問(wèn)題的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript:void(0)是什么意思及href=#與href=javascriptvoid(0)的區(qū)別
Javascript中void是一個(gè)操作符,該操作符指定要計(jì)算一個(gè)表達(dá)式但是不返回值,本文給大家介紹javascript:void(0)是什么意思及href=#與href=javascriptvoid(0)的區(qū)別,需要的朋友參考下2015-11-11深入理解JavaScript系列(1) 編寫(xiě)高質(zhì)量JavaScript代碼的基本要點(diǎn)
才華橫溢的Stoyan Stefanov,在他寫(xiě)的由O’Reilly初版的新書(shū)《JavaScript Patterns》(JavaScript模式)中,我想要是為我們的讀者貢獻(xiàn)其摘要,那會(huì)是件很美妙的事情2012-01-01JavaScript實(shí)現(xiàn)跨瀏覽器的添加及刪除事件綁定函數(shù)實(shí)例
這篇文章主要介紹了JavaScript實(shí)現(xiàn)跨瀏覽器的添加及刪除事件綁定函數(shù),采用純javascript實(shí)現(xiàn)jquery的bind及unbind添加與刪除事件綁定的技巧,具有很好的瀏覽器兼容性,需要的朋友可以參考下2015-08-08詳解使用Nuxt.js快速搭建服務(wù)端渲染(SSR)應(yīng)用
這篇文章主要介紹了詳解使用Nuxt.js快速搭建服務(wù)端渲染(SSR)應(yīng)用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03Uniapp?實(shí)現(xiàn)全民分銷(xiāo)功能原理解析
這篇文章主要介紹了Uniapp?實(shí)現(xiàn)全民分銷(xiāo)功能,本篇文章主要介紹全民分銷(xiāo)功能實(shí)現(xiàn)原理,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06layui(1.0.9)文件上傳upload,前后端的實(shí)例代碼
今天小編就為大家分享一篇layui(1.0.9)文件上傳upload,前后端的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09JS實(shí)現(xiàn)根據(jù)詳細(xì)地址獲取經(jīng)緯度功能示例
這篇文章主要介紹了JS實(shí)現(xiàn)根據(jù)詳細(xì)地址獲取經(jīng)緯度功能,涉及javascript與百度地圖接口交互進(jìn)行地址經(jīng)緯度查詢(xún)的相關(guān)操作技巧,需要的朋友可以參考下2019-04-04