ajax 同步_第3部分-0:同步和异步,还有回调需要了解一下
同步是什么
異步是什么
異步場(chǎng)景:
(1)定時(shí)任務(wù)
(2)網(wǎng)絡(luò)請(qǐng)求:Ajax 、圖片加載 全面分析前端的網(wǎng)絡(luò)請(qǐng)求方式
(3)事件綁定
異步產(chǎn)生知識(shí)點(diǎn):
進(jìn)程和線程
單線程 記一次 Vue 移動(dòng)端活動(dòng)倒計(jì)時(shí)優(yōu)化
JavaScript的執(zhí)行機(jī)制 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制
+ Event loop(定時(shí)任務(wù))最后一次搞懂 Event Loop
+ 宏任務(wù) 、微任務(wù) :【半月刊 3】前端高頻面試題及答案匯總
javascript的宏任務(wù)和微任務(wù)
解決異步:回調(diào)
回調(diào)產(chǎn)生的回調(diào)地域 —— Promise
手寫(xiě)一個(gè)promise :手寫(xiě) Promise
(一)同步、異步 和回調(diào)
一、同步和異步是什么
同步:「 等待結(jié)果 」
異步:「 不等待結(jié)果 」直接進(jìn)行下一步 (不等但之后還想要拿到結(jié)果)
回調(diào): 回調(diào)是解決異步的常用方式,但回調(diào)不等于異步,回調(diào)也可以用在同步
注意幾點(diǎn)問(wèn)題:
- 這里我們所說(shuō)的同步、異步,都屬于函數(shù)的范疇,也常常被稱(chēng)之為同步函數(shù)(任務(wù))、異步函數(shù)(任務(wù))
- 異步常使用回調(diào)的方式,但異步不是回調(diào)。
- 回調(diào)既可以用來(lái)異步,也可以用來(lái)同步,也可以是異步。
畫(huà)一張同步&異步工作的示意圖:
可以看出,用了異步之后,JS 的空閑時(shí)間多了許多。
但是注意,在 JS 空閑的這段時(shí)間,實(shí)際上是瀏覽器中的計(jì)時(shí)器在工作(很有可能是每過(guò)一段時(shí)間檢查是否時(shí)間到了,具體要看 Chrome 代碼)
(二)遇到異步實(shí)例和解決方法
一、異步實(shí)例
1、前端經(jīng)常遇到的異步
圖片加載是需要時(shí)間的
剛開(kāi)始是直接獲取寬度
<!DOCTYPE html> <html> <head><meta charset="utf-8"><title>JS Bin</title> </head> <body><img src="http://imgsrc.baidu.com/image/c0%3Dshijue1%2C0%2C0%2C294%2C40/sign=8d22d2f93cd12f2eda08a62327abbf17/b8389b504fc2d56273fb63c3ed1190ef76c66c29.jpg" alt=""> </body> </html>var w = document.getElementsByTagNames('img')[0].width console.log(w)先畫(huà)一個(gè)示意圖:
由此可知,js在img網(wǎng)絡(luò)請(qǐng)求還沒(méi)執(zhí)行完的時(shí)候緊隨執(zhí)行,可知為異步
//先獲取網(wǎng)絡(luò)請(qǐng)求前img信息,為空對(duì)象 var img = document.getElementsByTagName('img')[0]img等待網(wǎng)絡(luò)請(qǐng)求完成后,獲取完整圖片信息后,便會(huì)觸發(fā)一個(gè)onload事件:
//等待完成之后執(zhí)行的內(nèi)容:img如果加載成功,就會(huì)觸發(fā)一個(gè)onload的事件,獲取它的寬度并打印出寬度 img.onload = function(){var w =img.widthconsole.log(w) }?完整代碼:
var img = document.getElementsByTagName('img')[0]//異步不等繼續(xù)執(zhí)行,異步回調(diào)函數(shù):等待到網(wǎng)絡(luò)請(qǐng)求完成后觸發(fā)onload事件 img.onload = function(){var w =img.widthconsole.log(w) } console.log(img.width)//或者 document.getElementsByTagNames('img')[0].onload = function(){console.log(this.width) // 寬度不為 0console.log('real done') } console.log('done')總結(jié):異步想拿到一個(gè)結(jié)果,常采用監(jiān)聽(tīng)一個(gè)事件,然后告知(這個(gè)事件的完成時(shí)間不確定,不可預(yù)測(cè)),那就可以掛一個(gè)函數(shù)在onload上,等你請(qǐng)求完成,調(diào)用一下onload事件,此為回調(diào)函數(shù)。
2、面試題中的異步
let liList = document.querySelectorAll('li') for(var i=0; i<liList.length; i++){liList[i].onclick = function(){console.log(i)} }//獲取dom結(jié)構(gòu)的所有l(wèi)i元素,獲取li的長(zhǎng)度去遍歷,每一個(gè)點(diǎn)擊后都能打印出東西把 var i 改成 let 就可以破解:https://zhuanlan.zhihu.com/p/28140450
先讓我運(yùn)行上面的js代碼:
這里,js代碼運(yùn)行,還要注意一個(gè)技巧:變量提升,即
var i = 0//關(guān)鍵點(diǎn):變量提升為 var i i =0那么,代碼如下:
let liList = document.querySelectorAll('li') var i //i是貫穿6次循環(huán)的一個(gè)變量(沒(méi)有多個(gè)) for(i=0; i<liList.length; i++){liList[i].onclick = function(){console.log(i)} }畫(huà)一個(gè)時(shí)序圖:
可以看出,js執(zhí)行代碼時(shí),當(dāng)i=5,i++結(jié)果為6的時(shí)候,并不小于liList.length,那么就跳出該循環(huán),最后輸出結(jié)果:i=6。js代碼執(zhí)行完,用戶(hù)開(kāi)始操作他的鼠標(biāo),假設(shè)等待3ms后,執(zhí)行click li,當(dāng)你最先click的時(shí)候(i=0,liList[0],此時(shí)js已經(jīng)執(zhí)行完代碼,輸出i = 6 ),而不是在綁定事件的時(shí)候打印出幾,就是幾。
在這里,我們有必要知道,異步函數(shù)以下綁定事件為:
XXXX.onclick function(){console.log(i)}瀏覽器并未等該異步執(zhí)行,直接進(jìn)入for循環(huán),直接將i=6輸出,然后第一個(gè)click才出現(xiàn),瀏覽器不會(huì)等click出現(xiàn)才去打印 i 值
如何解決?——使用let
假設(shè)你已經(jīng)知道let(不懂看這篇文章):
方應(yīng)杭:我用了兩個(gè)月的時(shí)間才理解 let?zhuanlan.zhihu.com將代碼var i改為let:
let liList = document.querySelectorAll('li') for(let i=0; i<liList.length; i++){liList[i].onclick = function(){console.log(i)} }運(yùn)行如下:
為何let能一一打印出結(jié)果呢?即let不會(huì)被提升到外面,let作用域即處于for循環(huán)函數(shù)里,即每一次循環(huán),liList[i]都有一個(gè)新的 i 值。let會(huì)在每一次進(jìn)入循環(huán)時(shí),產(chǎn)生一個(gè)分身i1-i6.
畫(huà)一個(gè)運(yùn)行圖:【缺】
3、AJAX 中的異步(必須)
//同步的Ajax let request = $.ajax({url: '.', //1、獲取當(dāng)前 urlasync: false })//2、此時(shí),該函數(shù)會(huì)等待請(qǐng)求完成才執(zhí)行下一步 console.log(request.responseText)//打印出這個(gè)請(qǐng)求的響應(yīng)文本,即當(dāng)前html頁(yè)面//responseText:響應(yīng)文本相當(dāng)于同步,js在該函數(shù)中什么都沒(méi)做,但就是停了幾十ms,如同一個(gè)呆滯的人白白浪費(fèi)了一段空閑時(shí)間。
而Ajax的異步如何做?——async:true
$.ajax({url: '.',async: true,success: function(responseText){console.log(responseText)}//表示:如果請(qǐng)求返回回來(lái),麻煩調(diào)用以下success這個(gè)函數(shù),然后把得出的結(jié)果打印出來(lái) }) console.log('請(qǐng)求發(fā)送完畢')在控制臺(tái)上,模擬一個(gè)網(wǎng)速很慢的操作:Network——slow 3G,如圖:
首先ajax函數(shù)會(huì)發(fā)一個(gè)請(qǐng)求,繼續(xù)執(zhí)行第二句console.log,這就是ajax中的異步。在這里,先不管ajax里的請(qǐng)求成功或失敗,直接執(zhí)行第二句代碼。不等,即為異步;而等則是一定要拿到結(jié)果才進(jìn)行下一步。時(shí)間不到,異步絕對(duì)拿不到結(jié)果。
畫(huà)一下圖:
如果我們把它改為同步:async:false,并模擬一個(gè)很慢的網(wǎng)速:Network——add,參數(shù)設(shè)置如下:
同步之后,代碼運(yùn)行演示如下:
二、異步的形式
從上面的例子中:可以通過(guò)綁定onload事件獲取寬度大小,或者ajax中的success函數(shù)
一般,有兩種方式拿到異步結(jié)果
1、傻逼方法:輪詢(xún)
2、正規(guī)方法:回調(diào)
回調(diào)的形式
- Node.js 的 error-first 形式
- jQuery 的 success / error 形式
- jQuery 的 done / fail / always 形式
- Prosmise 的 then 形式
三、如何處理異常?
自己返回 Promise
function ajax(){return new Promise((resolve, reject)=>{做事如果成功就調(diào)用 resolve如果失敗就調(diào)用 reject}) }var promise = ajax() promise.then(successFn, errorFn)Promise 深入閱讀:http://www.cnblogs.com/hustskyking/p/promise.html Promise/A+ 規(guī)范:https://segmentfault.com/a/1190000002452115
async / await
function buyFruit(){return new Promise((resolve, reject)=>{做事如果成功就調(diào)用 resolve如果失敗就調(diào)用 reject}) } var promise = await ajax()async functon fn(){var result = await buyFruit()return result } var r = await fn() console.log(r)前言:
在梳理知識(shí)點(diǎn)的時(shí)候,發(fā)現(xiàn)作為瀏覽器渲染中的機(jī)制之一——異步加載機(jī)制,當(dāng)用戶(hù)訪問(wèn)站點(diǎn),需要下載各種資源,例如JS腳本,CSS,圖片,iframe等,它是實(shí)現(xiàn)現(xiàn)代網(wǎng)站進(jìn)行加載頁(yè)面時(shí)一種必不可少的手段。查資料加上老師拓展課程均對(duì)于異步加載機(jī)制還有很多方法可以說(shuō),故抽出來(lái)單獨(dú)進(jìn)行一個(gè)知識(shí)點(diǎn)的梳理。
async和defer / setTimeout / 綁定事件 /Ajax // 回調(diào)函數(shù)//函數(shù)節(jié)流
了解js腳本異步加載前,我們有必要先了解一下瀏覽器在頁(yè)面樣式和js的作用下出現(xiàn)的兩種頁(yè)面常見(jiàn)場(chǎng)景:白屏和fouc(無(wú)樣式內(nèi)容閃爍)。(一)白屏vsFOUC 和CSS和JS的位置順序
一、白屏vsFOUC
1、即指影響瀏覽器頁(yè)面加載順序的兩種場(chǎng)景
- 白屏:特指一種場(chǎng)景,打開(kāi)頁(yè)面是一片白色,突然頁(yè)面出現(xiàn),樣式正確。那么一片白色的時(shí)間,則稱(chēng)之為白屏。
- FOUC (Flash of Unstyled Content) :無(wú)樣式內(nèi)容閃爍,網(wǎng)速情況差,打開(kāi)頁(yè)面時(shí)仍有樣式,之后樣式時(shí)有時(shí)無(wú),甚至一開(kāi)始并無(wú)出現(xiàn)樣式,突然樣式恢復(fù)。(常出現(xiàn)在firefox瀏覽器)
此類(lèi)現(xiàn)象,在不同瀏覽器進(jìn)行的資源加載和頁(yè)面渲染時(shí),所采用的不同的處理方式,并不是bug。
2、寫(xiě)一個(gè)server,驗(yàn)證白屏和fouc效果
在樣式文件index.html中
//index.html <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>fouc & 白屏</title><!--在下面模擬一個(gè)延時(shí)裝置--> <link rel="stylesheet" href="b.css?t=10"> //設(shè)置這個(gè)工具,當(dāng)請(qǐng)求該文件時(shí),服務(wù)器會(huì)延遲請(qǐng)求10s再去加載這個(gè)資源,以此可以模擬一個(gè)網(wǎng)速特別慢的情況<link rel="stylesheet" href="a.css?t=3"> </head> <body><p>hello</p><p>饑人谷</p> <!-- <script src="A.js?t=5"></script> --><img src="https://jirengu.com/data/upload/2017/0118/17/587f39fba695a.png" alt=""><!-- <link rel="stylesheet" href="c.css?t=6"> --><!-- <script src="http://a.jrg.com:8080/B.js?t=4" ></script> <script src="http://b.jrg.com:8080/A.js?t=8" ></script> --></body> </html>(1)關(guān)于白屏,需要注意的是,瀏覽器對(duì)于樣式和js的處理,即CSS 和 JS 放置順序。推薦:將樣式放在 <head>里面,將JS放在<body>內(nèi)部下方。
如上面代碼所示,html頁(yè)面里引入了兩個(gè)css:a.css和b.css。b.css引用了c.ss(@import "./c.css?t=5";)b.css中加入了一個(gè)10s的延時(shí)文件(<link rel="stylesheet" href="b.css?t=10">),加載這個(gè)10s的css樣式文件,瀏覽器是如何完成加載工作,有兩種方式:
第1種:html解析完成,此時(shí)10s延時(shí)的css文件先不管,先展示<body>里所展示的內(nèi)容,等css文件全加載后再去計(jì)算樣式,再去重新渲染一次
第2種:即使html的dom樹(shù)已經(jīng)解析、渲染都完成,對(duì)未加載完成的樣式都必須等待,即css樣式要全部加載、獲取,img資源加載完成,此時(shí)底部JS立刻執(zhí)行,才一次性展示出頁(yè)面。例子中展示這種方法,即為白屏很久的原因。
(2)不同瀏覽器的不同處理機(jī)制所出現(xiàn)的場(chǎng)景不同
A、白屏場(chǎng)景(常出現(xiàn)在chrome):打開(kāi)一個(gè)國(guó)外網(wǎng)站,使用國(guó)外服務(wù)器,嵌在css的字體使用的是谷歌字體,運(yùn)行特別慢,等了好久突然出現(xiàn)頁(yè)面樣式效果。這是因?yàn)轫?yè)面需要等待css樣式加載所有完成,甚至出現(xiàn)404加載失敗,最后才展示出頁(yè)面。那么那段加載時(shí)間,等待了幾秒左右的白色一片的頁(yè)面,就是白屏
B、Fouc場(chǎng)景(常出現(xiàn)在Firefox):一開(kāi)始的時(shí)候,先讓你看見(jiàn)樣式,如字的小號(hào)樣式,樣式加載完后看到所規(guī)定字號(hào)的大字。對(duì)用戶(hù)來(lái)說(shuō),同樣的樣式,突然從小變大,則這個(gè)場(chǎng)景就是Fouc(無(wú)樣式內(nèi)容閃爍)。
二、CSS和JS的位置順序
總結(jié):不管是css樣式,還是js文件,只要加長(zhǎng)延時(shí),都會(huì)造成白屏1、CSS 和 JS 最佳放置順序
- 使用 link 標(biāo)簽將樣式表放在頂部
- 將JS放在底部
2、場(chǎng)景:假設(shè)JS文件頁(yè)面頂部:
- JS腳本會(huì)阻塞后面內(nèi)容的呈現(xiàn)
- JS腳本會(huì)阻塞其后組件(如圖片)的下載
- JS加載時(shí)間過(guò)長(zhǎng),css需等待,則會(huì)出現(xiàn)一段時(shí)間白屏
場(chǎng)景說(shuō)明:引入一個(gè)JS文件在頂部,設(shè)置一個(gè)延時(shí)時(shí)間。
加載順序:css—js—img—全部獲取到展現(xiàn)頁(yè)面效果
此時(shí),img和css加載時(shí)會(huì)并發(fā)加載,即如一個(gè)域名下同時(shí)加載兩個(gè)文件(并發(fā)是有限度的),加載在頂部的js時(shí),會(huì)禁用并發(fā)img和css,并阻止其他內(nèi)容下載和渲染。
js并不影響css加載,但是會(huì)影響css樣式的一個(gè)計(jì)算。當(dāng)js加載時(shí),css已經(jīng)獲取到(不過(guò)此時(shí)頁(yè)面還是一片空白),直到j(luò)s獲取立即執(zhí)行后,圖片立刻出現(xiàn),頁(yè)面才展示效果。所以js文件放入頁(yè)面頂部<head>里,也會(huì)導(dǎo)致白屏現(xiàn)象出現(xiàn)
3、JS加載特點(diǎn)總結(jié)
(1)優(yōu)先加載js文件,加載后js立刻去執(zhí)行,展示頁(yè)面(CSS樣式則是全部加載完,然后一次性展示出頁(yè)面)
注:css放前面,優(yōu)先加載;若放后面,其他資源則會(huì)阻礙css加載,那么時(shí)機(jī)就太晚。
(2)由于渲染線程和js腳本線程是互斥的,白屏是渲染進(jìn)程被阻塞的原因,當(dāng)碰到script標(biāo)簽的時(shí)候,會(huì)先執(zhí)行js腳本,然后再渲染。
(放頂部時(shí))JS加載時(shí)機(jī)過(guò)晚導(dǎo)致一系列問(wèn)題,腳本會(huì)阻塞后面內(nèi)容的呈現(xiàn)、腳本會(huì)阻塞其后組件的下載(主要指img資源下載)、白屏等。而(放底部)則可以先讓其他先加載完成,JS立刻執(zhí)行的特點(diǎn)可以“掃尾”最后的頁(yè)面效果
(3)JS腳本操作頁(yè)面上的html+css元素,(放頂部時(shí))JS先執(zhí)行,元素都未加載到(即不存在),未出現(xiàn)在文檔流中【加載,這里指資源加載和資源是否出現(xiàn)在文檔流中】,所以也不能操作相應(yīng)JS功能,此時(shí)后臺(tái)將會(huì)報(bào)錯(cuò)。
(4)(放頂部時(shí))其他JS若作為一種框架語(yǔ)言,則能提前形成一個(gè)初步的框架有效構(gòu)成頁(yè)面結(jié)構(gòu)。
三、解決方法:JS腳本的異步加載
1、一個(gè)問(wèn)題?
即一個(gè)放在<head>的js文件,如下:
<script src="script.js"></script>原本放在頂部的這個(gè)js文件,會(huì)提前加載,如何使它在頂部仍然稍后加載呢?
2、解決方法: async和defer
(1)作用:
沒(méi)有 defer 或 async,瀏覽器會(huì)立即加載并執(zhí)行指定的腳本,“立即”指的是在渲染該 script 標(biāo)簽之下的文檔元素之前,也就是說(shuō)不等待后續(xù)載入的文檔元素,讀到就加載并執(zhí)行。也就是說(shuō),使用defer 或 async后能夠改變這種加載、執(zhí)行的時(shí)機(jī)。
常應(yīng)用在引用了廣告和統(tǒng)計(jì)的頁(yè)面中,不會(huì)影響、堵塞,更不會(huì)影響到到頁(yè)面其他元素
(2) async HTML5里為script標(biāo)簽里新增了async屬性,用于異步加載腳本:不保證順序(獨(dú)立的個(gè)體)
<script async src="script.js"></script> 或 <script type="text/javascript" src="alert.js" async="async"></script>瀏覽器解析到HTML里的該行script標(biāo)簽,發(fā)現(xiàn)指定為async,會(huì)異步下載解析執(zhí)行腳本(即加載后續(xù)文檔元素的過(guò)程將和 script.js 的加載并行進(jìn)行)。
頁(yè)面的DOM結(jié)構(gòu)里假設(shè)<script>在img之前,如果你的瀏覽器支持async的話,就會(huì)異步加載腳本。此時(shí)DOM里已經(jīng)有img了,所以腳本里能順利取到img的src并彈框。 (3)defer script標(biāo)簽里可以設(shè)置defer,表示延遲加載腳本:
腳本先不執(zhí)行,延遲到文檔解析和顯示后執(zhí)行,有順序
<script defer src="script.js"></script> 或 <script type="text/javascript" src="alert.js" defer="defer"></script>瀏覽器解析到HTML里該行script標(biāo)簽,發(fā)現(xiàn)指定為defer,會(huì)暫緩下載解析執(zhí)行腳本,等到頁(yè)面文檔解析并加載執(zhí)行完畢后,才會(huì)加載該腳本(更精確地說(shuō),是在DOM樹(shù)構(gòu)建完成后,在DOMContentLoaded 事件觸發(fā)前,加載defer的腳本)。 頁(yè)面的DOM結(jié)構(gòu)里假設(shè)script在img圖片之前,如果你的瀏覽器支持defer的話,就會(huì)延遲到頁(yè)面加載完后才下載腳本。此時(shí)DOM里已經(jīng)有img元素了,所以腳本里能順利取到img的src并彈框。
總結(jié):JS實(shí)質(zhì)采用一種可以更自由地選擇加載時(shí)機(jī)和任何位置,讓處于頂部的js文件能夠像在底部時(shí),在頁(yè)面必要元素加載完成時(shí)進(jìn)行“異步”加載。
總結(jié)
以上是生活随笔為你收集整理的ajax 同步_第3部分-0:同步和异步,还有回调需要了解一下的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 清华大学上海交大,复制粘贴般的优秀!
- 下一篇: 上午写了一段代码,下午就被开除了,奇怪的