JS異步任務和異步加載

剛開始我區(qū)分不了異步任務和異步加載,感覺兩者是很像的東西。
隨著時間發(fā)現(xiàn)兩者是形容不同的事物。
比如異步任務,講的是event loop的執(zhí)行規(guī)則,是怎么讓function異步執(zhí)行。
而異步加載則講的更多的是在渲染頁面時,如何讓瀏覽器在下載JS的同時,還會進行后續(xù)頁面的處理。

一、JS異步任務

JS是單線程語言,如果都按照順序進行,往往會出現(xiàn)瀏覽器無響應的情況,所以就需要異步的形式。

1.JS中任務的形式

JS中所有的任務可以分為兩種:同步任務和異步任務。

  • 同步任務:在主線程上排隊執(zhí)行的任務,只有前一個任務執(zhí)行完畢,才能執(zhí)行后一個任務。
    這種模式的好處是實現(xiàn)起來比較簡單,執(zhí)行環(huán)境相對單純。
    壞處是只要有一個任務耗時很長,后面的任務都必須排隊等著,會拖延整個程序的執(zhí)行。常見的瀏覽器無響應(假死),往往就是因為某一段Javascript代碼長時間運行(比如死循環(huán)),導致整個頁面卡在這個地方,其他任務無法執(zhí)行。

  • 異步任務:不進入主線程,而進入任務隊列中的任務,只有任務隊列通知主線程,某個異步任務可以執(zhí)行了,這個任務才會進入主線程執(zhí)行。

2.異步的解決方案

1. Promise
function ajax(url){
    return new Promise(function(resolve,reject){
        var xhr=new XMLHttpRequest();
        xhr.onload=function(){
            resolve(this.responseText);
        };
        xhr.onerror=reject;
        xhr.open('GET',url);
        xhr.send();
    });
}
ajax('/echo/json')
    .then(function(result){...})
    .then(function(){...})
    .catch(function(){...});

Promise代表了一個異步操作,可以將異步對象和回調(diào)函數(shù)脫離開來,通過.then方法在這個異步操作上綁定回調(diào)函數(shù),Promise可以讓我們通過鏈式調(diào)用的方法去解決回調(diào)嵌套的問題,而且由于promise.all這樣的方法存在,可以讓同時執(zhí)行多個操作變得簡單。

promise對象存在三種狀態(tài):

1)Fulfilled:成功狀態(tài)
2)Rejected:失敗狀態(tài)
3)Pending:既不是成功也不是失敗狀態(tài),可以理解為進行中狀態(tài)

promise對象的兩個重要方法:resolve/reject

1).resolve方法可以使Promise對象的狀態(tài)改變?yōu)槌晒?,同時傳遞一個參數(shù)用于后續(xù)成功后的操作。
2).reject方法可以將Promise對象的狀態(tài)改變?yōu)槭。瑫r將錯誤信息傳遞到后續(xù)錯誤處理的操作。
3).then可以使用鏈式調(diào)用,原因在于:每一次執(zhí)行該方法時總會返回一個Promise對象。
另外,在then的函數(shù)當中的返回值,可以作為后續(xù)操作的參數(shù)(例如:.then(return a).then(console.log(a+b)))

promise異常處理

如果代碼異步操作拋出錯誤,會調(diào)用catch方法指定的回調(diào)函數(shù),處理這個錯誤,而且then方法指定的回調(diào)函數(shù),如果運行中拋出錯誤,也會被catch捕獲。Promise對象的錯誤具有“冒泡”性質(zhì),會一直向后傳遞,直到被捕獲為止,也就是說,錯誤總是會被下一個catch語句捕獲。

理解Promise用法的關(guān)鍵點:
  • 1.then方法是Promise實例的方法,即Promise.prototype上的,它的作用是為Promise實例添加狀態(tài)改變時的回調(diào)函數(shù),這個方法的第一個參數(shù)是resolved狀態(tài)的回調(diào)函數(shù),第二個參數(shù)(可選)是rejected狀態(tài)的回調(diào)函數(shù)。
  • 2.鏈式中的第二個then開始,它們的resolve中的參數(shù),是前一個then中resolve的return語句的返回值。
  • 3.關(guān)于執(zhí)行順序:Promise在實例化的時候就會執(zhí)行,也就是如果Promise的實例化語句中函數(shù)console.log輸出語句,它會比then中的先執(zhí)行。Promise.all中傳入的Promise對象的數(shù)組(假設(shè)為p1、p2),即使p2的運行速度比p1快,Promise.all方法仍然會按照數(shù)組中的順序?qū)⒔Y(jié)果返回。
Promise的缺點:

1.當處于未完成狀態(tài)時,無法確定目前處于哪一階段。
2.如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部的錯誤不會反映到外部。
3.無法取消Promise,一旦新建它就會立即執(zhí)行,無法中途取消。

2.async/await

很多人說async/await是異步編程的終極解決方案、
JavaScript 的 async/await 實現(xiàn),離不開 Promise。

async:定義異步函數(shù)

1)自動把函數(shù)轉(zhuǎn)換為Promise
2)當調(diào)用異步函數(shù)時,函數(shù)返回值會被resolve處理
3)異步函數(shù)內(nèi)部可以使用await

await:暫停異步函數(shù)的執(zhí)行

1)當使用在Promise前面時,await等待Promise完成,并返回Promise的結(jié)果
2)await只能和Promise一起使用,不能和callback一起使用
3)await只能用在async函數(shù)中

async/await并不會取代promise,因為async/await底層依然使用promise。
下面有兩個例子:

async function getABC(){
    let A = await getValueA(); // getValueA 花費 2 秒
    let B = await getValueB(); // getValueA 花費 4 秒
    let C = await getValueC(); // getValueA 花費 3 秒
    return A*B*C
}

每次遇到 await 關(guān)鍵字時,Promise 都會停下在,一直到運行結(jié)束,所以總共花費是 2+4+3 = 9 秒

async function getABC() {
    // Promise.all() 允許同時執(zhí)行所有的異步函數(shù)
    let results = await Promise.all([ getValueA, getValueB, getValueC ]); 
    return results.reduce((total,value) => total * value);
}

函數(shù)總耗時為 4 秒(getValueB 的耗時)。

Async 的價值在于用寫同步的方式寫異步,1避免了阻塞,2必免寫回調(diào)

二、異步加載

JS在默認情況下是以同步模式(又稱阻塞模式)加載的,瀏覽器對于代碼請求的資源都是瀑布式的加載,而不是阻塞式的,但是JS的執(zhí)行總是阻塞式的。

這會引起什么問題?如果在頁面中加載一些JS,但其中某個請求遲遲得不到響應,位于此JS后的JS將無法執(zhí)行,同時頁面渲染也不能繼續(xù)。

異步加載又被稱為非阻塞加載,瀏覽器在下載JS的同時,還會進行后續(xù)頁面處理。

1.如何實現(xiàn)JS異步加載

  • 動態(tài)生成<script>標簽:這種方法可以兼容所有瀏覽器。
  • async屬性
    async是HTML5的新屬性,該屬性規(guī)定一旦腳本可用,則會異步執(zhí)行(一旦下載完畢就會立刻執(zhí)行),需要注意的是,async屬性僅適用于外部腳本。
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' async="async"></script>
  • defer屬性
    defer屬性規(guī)定是否對該腳本的執(zhí)行進行延遲,知道頁面加載為止。
 <script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' defer="defer"></script

來分析一下async和defer屬性:

a.如果沒有async屬性和defer屬性,那么瀏覽器會立即執(zhí)行當前的JS腳本,阻塞后面的腳本;
b.如果有async屬性,加載和渲染后續(xù)文檔的過程和當前JS的加載和執(zhí)行并行進行,它是亂序執(zhí)行的,不管你聲明的順序如何,只要它加載完了就會執(zhí)行。
c.如果有defer屬性,加載后續(xù)文檔元素的過程和JS的加載時并行進行的,但是JS的執(zhí)行是在所有元素解析完成之后進行的,而且它是按照加載順序執(zhí)行腳本的。

簡言之:async是亂序;defer是順序。

-$(document).ready
需要引用jquery
兼容所有瀏覽器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、Javascript實現(xiàn)異步編程的過程以及原理 1、為什么要用Javascript異步編程 眾所周知,Java...
    Ebony_7c03閱讀 932評論 0 2
  • 前言 我們知道Javascript語言的執(zhí)行環(huán)境是"單線程"。也就是指一次只能完成一件任務。如果有多個任務,就必須...
    浪里行舟閱讀 14,723評論 1 27
  • 由于JavaScript是單線程的語言,因此異步編程對于js的重要程度可想而知,可以說沒有異步的js程序寸步難行。...
    會飛小超人閱讀 1,038評論 0 7
  • jjcancan閱讀 224評論 0 0
  • 先生們,女士們,各位觀眾朋友們,又到了一周一次的牙齒治療日了,讓我們來看看,今天會發(fā)生什么事情呢,下面為患者自述…...
    Depressed豬閱讀 612評論 0 51

友情鏈接更多精彩內(nèi)容