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