學(xué)習(xí)教材為:https://wangdoc.com/javascript/async/index.html
1 如何理解JS的單線程模型?單線程模型優(yōu)劣勢?
單線程模型指的是,JavaScript 只在一個線程上運(yùn)行。也就是說,JavaScript 同時只能執(zhí)行一個任務(wù),其他任務(wù)都必須在后面排隊等待。
2 JavaScript 引擎都有哪些線程?
JavaScript 引擎有多個線程,單個腳本只能在一個線程上運(yùn)行(稱為主線程),其他線程都是在后臺配合。
3 什么是同步任務(wù)和異步任務(wù)?
同步任務(wù)是那些沒有被引擎掛起、在主線程上排隊執(zhí)行的任務(wù)。只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù)。
異步任務(wù)是那些被引擎放在一邊,不進(jìn)入主線程、而進(jìn)入任務(wù)隊列的任務(wù)。只有引擎認(rèn)為某個異步任務(wù)可以執(zhí)行了(比如 Ajax 操作從服務(wù)器得到了結(jié)果),該任務(wù)(采用回調(diào)函數(shù)的形式)才會進(jìn)入主線程執(zhí)行。排在異步任務(wù)后面的代碼,不用等待異步任務(wù)結(jié)束會馬上運(yùn)行,也就是說,異步任務(wù)不具有“堵塞”效應(yīng)。
4 如何理解JS引擎提供的任務(wù)隊列?
首先,主線程會去執(zhí)行所有的同步任務(wù)。等到同步任務(wù)全部執(zhí)行完,就會去看任務(wù)隊列里面的異步任務(wù)。如果滿足條件,那么異步任務(wù)就重新進(jìn)入主線程開始執(zhí)行,這時它就變成同步任務(wù)了。等到執(zhí)行完,下一個異步任務(wù)再進(jìn)入主線程開始執(zhí)行。一旦任務(wù)隊列清空,程序就結(jié)束執(zhí)行。
異步任務(wù)的寫法通常是回調(diào)函數(shù)。一旦異步任務(wù)重新進(jìn)入主線程,就會執(zhí)行對應(yīng)的回調(diào)函數(shù)。如果一個異步任務(wù)沒有回調(diào)函數(shù),就不會進(jìn)入任務(wù)隊列,也就是說,不會重新進(jìn)入主線程,因?yàn)闆]有用回調(diào)函數(shù)指定下一步的操作。
5 什么是事件循環(huán)?
JavaScript 引擎怎么知道異步任務(wù)有沒有結(jié)果,能不能進(jìn)入主線程呢?答案就是引擎在不停地檢查,一遍又一遍,只要同步任務(wù)執(zhí)行完了,引擎就會去檢查那些掛起來的異步任務(wù),是不是可以進(jìn)入主線程了。這種循環(huán)檢查的機(jī)制,就叫做事件循環(huán)(Event Loop)。
6 異步操作都有哪些模式?
- 回調(diào)函數(shù)是異步操作最基本的方法。
回調(diào)函數(shù)的優(yōu)點(diǎn)是簡單、容易理解和實(shí)現(xiàn),缺點(diǎn)是不利于代碼的閱讀和維護(hù),各個部分之間高度耦合(coupling),使得程序結(jié)構(gòu)混亂、流程難以追蹤(尤其是多個回調(diào)函數(shù)嵌套的情況),而且每個任務(wù)只能指定一個回調(diào)函數(shù)。 - 另一種思路是采用事件驅(qū)動模式。異步任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某個事件是否發(fā)生。
- 事件完全可以理解成“信號”,如果存在一個“信號中心”,某個任務(wù)執(zhí)行完成,就向信號中心“發(fā)布”(publish)一個信號,其他任務(wù)可以向信號中心“訂閱”(subscribe)這個信號,從而知道什么時候自己可以開始執(zhí)行。這就叫做”發(fā)布/訂閱模式”(publish-subscribe pattern),又稱“觀察者模式”(observer pattern)。
7 什么是串行執(zhí)行和并行執(zhí)行?
- 我們可以編寫一個流程控制函數(shù),讓它來控制異步任務(wù),一個任務(wù)完成以后,再執(zhí)行另一個。這就叫串行執(zhí)行。
- 流程控制函數(shù)也可以是并行執(zhí)行,即所有異步任務(wù)同時執(zhí)行,等到全部完成以后,才執(zhí)行final函數(shù)。
8 如何理解觀察者模式?
事件完全可以理解成“信號”,如果存在一個“信號中心”,某個任務(wù)執(zhí)行完成,就向信號中心“發(fā)布”(publish)一個信號,其他任務(wù)可以向信號中心“訂閱”(subscribe)這個信號,從而知道什么時候自己可以開始執(zhí)行。這就叫做”發(fā)布/訂閱模式”(publish-subscribe pattern),又稱“觀察者模式”(observer pattern)。
9 JavaScript 提供定時執(zhí)行代碼功能的相關(guān)函數(shù)有哪些?
- setTimeout函數(shù)用來指定某個函數(shù)或某段代碼,在多少毫秒之后執(zhí)行。它返回一個整數(shù),表示定時器的編號,以后可以用來取消這個定時器。
- setInterval函數(shù)的用法與setTimeout完全一致,區(qū)別僅僅在于setInterval指定某個任務(wù)每隔一段時間就執(zhí)行一次,也就是無限次的定時執(zhí)行。
- clearTimeout和clearInterval函數(shù),就可以取消對應(yīng)的定時器(setTimeout和setInterval)。
10 setTimeout 和 setInterval 的區(qū)別?
區(qū)別僅僅在于setInterval指定某個任務(wù)每隔一段時間就執(zhí)行一次,也就是無限次的定時執(zhí)行。
11 實(shí)現(xiàn)一個 debounce 函數(shù)?
$('textarea').on('keydown', debounce(ajaxAction, 2500));
function debounce(fn, delay){
var timer = null; // 聲明計時器
return function() {
var context = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}
12 setTimeout(f, 0) 的用途有哪些?
- 可以調(diào)整事件的發(fā)生順序。比如,網(wǎng)頁開發(fā)中,某個事件先發(fā)生在子元素,然后冒泡到父元素,即子元素的事件回調(diào)函數(shù),會早于父元素的事件回調(diào)函數(shù)觸發(fā)。如果,想讓父元素的事件回調(diào)函數(shù)先發(fā)生,就要用到setTimeout(f, 0)。
// HTML 代碼如下
// <input type="button" id="myButton" value="click">
var input = document.getElementById('myButton');
input.onclick = function A() {
setTimeout(function B() {
input.value +=' input';
}, 0)
};
document.body.onclick = function C() {
input.value += ' body'
};
- 另一個應(yīng)用是,用戶自定義的回調(diào)函數(shù),通常在瀏覽器的默認(rèn)動作之前觸發(fā)。比如,用戶在輸入框輸入文本,keypress事件會在瀏覽器接收文本之前觸發(fā)。因此,下面的回調(diào)函數(shù)是達(dá)不到目的的。
// HTML 代碼如下
// <input type="text" id="input-box">
document.getElementById('input-box').onkeypress = function (event) {
this.value = this.value.toUpperCase();
}
- 另一個使用這種技巧的例子是代碼高亮的處理。如果代碼塊很大,一次性處理,可能會對性能造成很大的壓力,那么將其分成一個個小塊,一次處理一塊,比如寫成setTimeout(highlightNext, 50)的樣子,性能壓力就會減輕。
13 Promise 對象 與普通對象的區(qū)別與聯(lián)系?
14 Promise 實(shí)例具有哪三種狀態(tài)?
- 異步操作未完成(pending)
- 異步操作成功(fulfilled)
- 異步操作失?。╮ejected)
15 then() 用法有哪些?
// 寫法一
// 寫法一的f3回調(diào)函數(shù)的參數(shù),是f2函數(shù)的運(yùn)行結(jié)果。
f1().then(function () {
return f2();
});
// 寫法二
// 寫法二的f3回調(diào)函數(shù)的參數(shù)是undefined。
f1().then(function () {
f2();
});
// 寫法三
// 寫法三的f3回調(diào)函數(shù)的參數(shù),是f2函數(shù)返回的函數(shù)的運(yùn)行結(jié)果。
f1().then(f2());
// 寫法四
// 寫法四與寫法一只有一個差別,那就是f2會接收到f1()返回的結(jié)果。
f1().then(f2);
16 Promise 的優(yōu)點(diǎn)和缺點(diǎn)?
優(yōu)點(diǎn)
- 讓回調(diào)函數(shù)變成了規(guī)范的鏈?zhǔn)綄懛?,程序流程可以看得很清楚。它有一整套接口,可以?shí)現(xiàn)許多強(qiáng)大的功能,比如同時執(zhí)行多個異步操作,等到它們的狀態(tài)都改變以后,再執(zhí)行一個回調(diào)函數(shù);再比如,為多個回調(diào)函數(shù)中拋出的錯誤,統(tǒng)一指定處理方法等等。
- 它的狀態(tài)一旦改變,無論何時查詢,都能得到這個狀態(tài)。這意味著,無論何時為 Promise 實(shí)例添加回調(diào)函數(shù),該函數(shù)都能正確執(zhí)行。所以,你不用擔(dān)心是否錯過了某個事件或信號。如果是傳統(tǒng)寫法,通過監(jiān)聽事件來執(zhí)行回調(diào)函數(shù),一旦錯過了事件,再添加回調(diào)函數(shù)是不會執(zhí)行的。
缺點(diǎn)
- 編寫的難度比傳統(tǒng)寫法高,而且閱讀代碼也不是一眼可以看懂。你只會看到一堆then,必須自己在then的回調(diào)函數(shù)里面理清邏輯。