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