JS異步:執行原理與回呼
- 一、JS異步的執行原理
- 二、JS異步中的回呼
一、JS異步的執行原理
??我們知道JavaScript是單執行緒的,而瀏覽器是多執行緒的,單執行緒執行任務需要一個個排隊進行,假如一個任務需要很長時間執行(像ajax需要較長時間),會直接導致無回應,后面的任務一直在等待執行,這時候就需要用到異步,
??想了解異步,首先我們要知道瀏覽器有最基本的三個常駐執行緒: JS引擎執行緒,事件觸發執行緒,GUI渲染執行緒,
??其中JS引擎執行緒和事件觸發執行緒共同構成了一種事件回圈機制,而GUI渲染執行緒與JS引擎是互斥的,當JS引擎執行時GUI執行緒會被掛起,GUI更新保存在一個佇列中,當JS引擎空閑時,立即被執行,
??我們從它的事件回圈機制決議:
??JS引擎執行緒中分為同步和異步任務:
????1.同步任務全部通過主執行緒執行,形成執行堆疊,
????2.當有異步任務時交給異步行程(WebAPIs):包含事件觸發執行緒或者定時器執行緒等處理,形成任務佇列,
????3.當執行堆疊中的任務全部處理完成,主執行緒為空閑的時候,會從任務佇列中提取任務到執行堆疊中執行,
??通俗來說,JavaScript除了主執行緒之外還存在一個任務佇列,任務佇列存放需要異步執行的內容,執行完主執行緒后,就會不斷回圈掃描執行任務佇列的任務,直至佇列清空,
畫解:

??如圖小明因為學習耗時長會,如果沒做完就會一直無法玩DNF游戲了,就把學習放到了異步任務佇列中,等玩完游戲(主執行緒)再學習(任務佇列),期間母親添加學習事件(DOM事件),小明每完成一個學習任務就看看還有啥任務(回圈掃描),直至最后做完.
??下面再看一個例子(瀏覽器重繪不斷點擊按鈕):
let myData = null
//ajax請求
function ajax() {
//騰訊新冠實時資料介面,僅做學習
axios.get('https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=chinaDayList,chinaDayAddList,nowConfirmStatis,provinceCompare')
.then(data => {
console.log("ajax回傳成功");
myData = data.data
console.log(myData);
})
.catch(error => {
console.log("ajax回傳失敗");
})
}
console.log(myData);
ajax()
setTimeout(() => {
console.log('定時器');
}, 2000);
console.log(myData);
const btn = document.querySelector('button')
btn.onclick = () => {
console.log("點擊了");
}
null
null
ajax回傳成功
Object
點擊了
定時器
點擊了
??可以看到,console在主執行緒中是同步執行的,先執行,而在主執行緒外的任務佇列,存放著異步執行的內容,這里是setTimeout,ajax和DOM事件,按照任務佇列順序執行(回圈掃描佇列),
??
??為什么要回圈掃描呢?
??通過點擊事件可以看出,當用戶進行互動時(點擊事件,滾動事件,視窗大小變化事件等),會向事件回圈中的任務佇列添加新事件,然后等待執行,所以需要回圈掃描,
二、JS異步中的回呼
??既然異步都是放在最后的任務佇列執行,那么我們很多邏輯就難以實作,這時候我們需要處理這種異步邏輯,最常用的方式是回呼——回頭呼叫,
回呼函式:簡單來說就是,函式A中傳入函式B作為引數時,函式B即為A函式執行的回呼函式,回呼有嵌套回呼和鏈式回呼兩種,
??下面是回呼的一個簡單用法:
let myData = null
console.log(myData);
setTimeout(() => {
console.log('定時器');
}, 2000);
const btn = document.querySelector('button')
btn.onclick = () => {
console.log("點擊了");
}
let name = "張三"
function hr(callback) {
setTimeout(() => {
console.log(`我是${name}`);
callback();
}, 2001);
}
console.log(myData);
function gj() {
console.log(`${name}你好,我是李四,認識一下吧`);
}
hr(gj)
null
null
點擊了
定時器
我是張三
張三你好,我是李四,認識一下吧
點擊了
??很明顯的看到,當我們函式需要用到資料的時候就用到了回呼,這里用到的是異步回呼,
??回呼雖然是解決異步常用的方法,可是伴隨著JS日益復雜的需求,同步異步需要越來越多的回呼實作邏輯,同異步的混雜和過多的回呼嵌套和縮進使得代碼變得難以解讀和維護,形成“回呼地獄”,

??我們看一個例子:
const verifyUser = function(username, password, callback){
dataBase.verifyUser(username, password, (error, userInfo) => {
if (error) {
callback(error)
}else{
dataBase.getRoles(username, (error, roles) => {
if (error){
callback(error)
}else {
dataBase.logAccess(username, (error) => {
if (error){
callback(error);
}else{
callback(null, userInfo, roles);
}
})
}
})
}
})
};
大多數人光是看到上面的代碼就感受到了腦子凍結的滋味,如果一個專案里擁有上百個這樣的代碼塊,過一段時間,我相信連撰寫他的人都會頭疼,來到自己的專案就像是來到了地獄,
??最主要的是,與此同時回呼還存在信任問題,他把執行控制權交給了某個第三方(比如ajax),為了解決信任問題,我們必須在程式寫各種邏輯來解決回呼帶來的信任問題,
??·呼叫過早
??·呼叫過完
??·呼叫次數過多過少,沒有把需要的引數成功傳給回呼函式,
??·可能出現的錯誤被吞,
??可以發現寫特定邏輯來解決特定的信任問題,已經使得難度大于本身應用價值了,還會造成代碼冗雜,可讀性差等問題,
??綜上:回呼解決異步存在缺陷:
?????1)不符合人對任務處理的邏輯思維
?????2)回呼帶來的信任問題,
??面對回呼日益明顯的弊端,ES6更新了Promise用來解決異步問題,下一篇寫ES6——Promise,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/267143.html
標籤:其他
