[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()
~~~