[toc]
# 題1、JS 中哪些代碼是異步的?
答:AJAX(與服務器通信)、setTimeout、setInterval、setImmediate (node)、process.nextTick(node)。
# 題2、事件循環是怎么回事?
答:JS 把代碼分為三部分:同步代碼、宏任務代碼、微任務代碼。
執行順序是:同步-》微任務-》宏任務。
微任務代碼:node 中的 process.nextTick 等
宏任務代碼:ajax、setTimeout、setInterval 等。
JS 在執行代碼時分為三種任務:
- 同步代碼
- 異步代碼
- 宏任務(macrotask):
script(整體代碼),
AJAX、事件回調(鼠標鍵盤事件)、
setTimeout,
setInterval,
setImmediate(node獨有),
I/O,
UI rendering
- 微任務(microtask) (優先級更高)
process.nextTick(node獨有),
Promises.then,
Object.observe(廢棄),
MutationObserver
執行順序:
1. 先執行同步代碼
2. microtask (微任務)中的代碼
3. 宏任務中的代碼
~~~
代碼演示:只能在 node 環境中執行:
// 同步代碼
console.log(1)
// 宏任務代碼
setTimeout(()=>{
console.log(2)
}, 0)
// 微任務代碼(node環境專有)
process.nextTick(()=>{
console.log(3)
})
// 同步代碼
console.log(4)
// 輸出結果: 1432
~~~
# 題3、什么是回調地獄?如何解決?
答:在寫異步代碼時都需要使用回調函數,當回調的層次多時,代碼不容易編寫,如果出錯。
解決辦法:使用 ES6 中新出的 promise 以及 ES8(ES2017) 中新出 async...await 改成同步寫法。
ES6 --》 ES2015
ES7 --》 ES2016
ES8 --》 ES2017
~~~
// 代碼一、傳統多層嵌套回調函數(回調地獄)
setTimeout(()=>{
console.log(1)
setTimeout(()=>{
console.log(2)
setTimeout(()=>{
console.log(3)
setTimeout(()=>{
console.log(4)
}, 0)
}, 0)
}, 0)
}, 0)
代碼二、把所有的異步代碼封裝成 Promise 對象(ES6 的 Promise 寫法)
// 無法是哪種方法:首先要先把異步代碼封裝成一個 Promise 對象:
let p1 = function() {
return new Promise(()=>{
// 異步代碼
setTImeout(()=>{
console.log(1)
},0)
})
}
let p2 = function() {
return new Promise(()=>{
// 異步代碼
setTImeout(()=>{
console.log(2)
},0)
})
}
let p3 = function() {
return new Promise(()=>{
// 異步代碼
setTImeout(()=>{
console.log(3)
},0)
})
}
let p4 = function() {
return new Promise(()=>{
// 異步代碼
setTImeout(()=>{
console.log(4)
},0)
})
}
// 依次調用(避免回調地獄)
p1().then(()=>{
return p2()
}).then(()=>{
return p3()
}).then(()={
return p4()
})
代碼三、async...await (ES8出的)(以同步的方式寫代碼)
(async function(){
await p1()
await p2()
await p3()
await p4()
})()
~~~
# 題4、什么是 Promise ?干什么用的?
答:ES6中新出的。
用途:
1. 可以用來將一段 `異步的代碼` 封裝成一個 Promise 對象
2. 封裝完之后就可以使用 ES8 中的 async ... await 來使用同步的方式寫代碼了,不需要再寫回調函數
# 題5、(封裝 Promise簡易版)如何將異步代碼封裝成一個沒有返回值的 Promise 對象?
答:
簡易版本的封裝流程(沒有參數、沒有返回值):
1. 要定義一個函數
2. 在這個函數中創建一個 Promise 對象并返回 return new Promise
3. 把異步的代碼寫在 Promise 中
// 代碼演示:如何將異步代碼封裝成 Promise 對象
~~~
// 異步代碼
setTimeout(function(){
console.log('hello')
}, 0)
// 把上面的異步代碼封裝成一個 Promise 對象
function hello() {
return new Promise(function(){
setTimeout(function(){
console.log('hello')
}, 0)
})
}
~~~
// 代碼演示:使用 Promise 對象
~~~
// 方法一、ES6 中原始語法
hello().then(function(){
// 異步代碼執行完之后的操作
})
方法二、ES8 async ... awiat 高級語法
async function doHello() {
await hello() // 執行異步
// 后續操作
}
doHello()
~~~
# 題6、封裝帶返回值的 Promise 對象(完整)?
答:
~~~
// 模擬:一段代碼的代碼,這段代碼返回 100
setTimeout(function(){
// 返回 100 這個數據
return 100
}, 100)
// 封裝帶成功、失敗返回值的 Promise 對象
function getData() {
// 參數一、resolve,函數,成功時調用它來設置返回值
// 參數二、reject,函數,失敗時調用它業設置失敗的返回值
return new Promise(function(resolve, reject){
// 以下異步代碼執行完之后得到 100
setTimeout(function(){
// 模擬成功
let ok = true
// 判斷是否成功
if(ok) {
// 成功時返回 100
resolve(100)
} else {
// 失敗時返回錯誤信息
reject('用戶名不正確!')
}
}, 100)
})
}
// ES6 使用
getData().then(res=>{
console.log(res.data) // 100
}).catch(err=>{
console.log(err) // 用戶名不正確!
})
// ES8 使用
async function doData() {
let data = await getData().catch(err=>{console.log(err)})
console.log(data.data) // 100
}
doData()
~~~