JS 异步
- event loop
- promise
- async/await
- 微任务/宏任务
请描述event loop (事件循环/事件轮询)的机制
JS是单线程运行的 异步要基于回调来实现 event loop 就是异步回调的实现原理
call Stack 调用栈 同步代码在这一行一行执行, 遇到异步会先记录微任务/宏任务,如是微任务存放在微任务队列,如果是宏任务则移交至web APIs 中存放。 然后继续执行同步任务直至完成所有同步任务位置才进入下一阶段
MicroTaskqueue 存放微任务 同步任务 执行完后到这里执行微任务 微任务按队列中的顺序执行 直至执行完微任务后 进入下一阶段
Dom元素渲染 微任务队列的所有任务执行完后 如果有dom渲染要求的话,会进行dom元素的渲染,如果没有则跳过
web APIs 存放要等待执行的宏任务队列 等待的时间到了放进去callback queue
callback queue 存放宏任务的执行队列 同步任务-微任务-(dom渲染)完毕后 依靠 event loop 依次的按顺序从中提取任务到call Stack 中执行
promise 有哪三种状态 如何变化
- pending
- resolved
- rejected
状态的表现和变化
- pending->resolved
- pending-> rejected 变化不逆
- pending状态 不会触发then 和catch
- resolved状态 会触发后续的then 回调函数
- rejected状态 会触发后续的catch回调函数
可以直接定义Promise.resolved() 变量 用于.then() 回调操作 (try catch模式下)
then catch 对状态的影响
- resolved 成功状态只会触发then操作
- .then默认返回是resolved状态 如果报错则返回rejected状态
- .catch 成功的话还是默认resolved状态
async/await
- async/await 是包装了promise 对象的语法糖
- async 函数 返回的是一个promise对象
- await 相当于.then操作
- 如果async返回的是一个promise.rejected对象 且await 连接 则代码报错,后序代码将不在执行,如果没报错,则进入微任务队列后续代码
什么是宏任务和微任务 两者有什么区别
- 宏任务和微任务都属于异步的一种情况
- 宏任务有 setTimeout setInterval AJax dom事件
- 微任务有 promise async/await
- 微任务比宏任务先执行
手写Promise
class MyPromise {
state = 'pending' //pending fuifilled rejected
value = undefined //记录当前 成功时 传进来的值
reason = undefined //记录当前 失败时 传进来的原因
resolveCallbacks = []
rejectCallbacks = []
// 构造函数中传进来一个函数,立马执行,该函数有2个参数 一个resovle 一个rejected
constructor(fn) {
// 处理成功时候的回调函数 接受一个变量 用于更新当前value
const resolveHandler = (value) => {
//执行这个回调处理方法的时候必须只能是pending状态
if (this.state === 'pending') {
this.state = 'fulfilled'//执行的时候状态pending->fulfulled
this.value = value
this.resolveCallbacks.forEach(fn => fn())
}
}
// 处理失败时的回调函数 接受一个变量 用于更新当前reason
const rejectHandler = (reason) => {
//执行这个回调处理方法的时候必须只能是pending状态
if (this.state === 'pending') {
this.state = 'rejected'//执行的时候状态pending->rejected
this.reason = reason
this.rejectCallbacks.forEach(fn => fn())
}
}
//采用try catch 方法以防传进来的函数存在问题而报错
try {
fn(resolveHandler, rejectHandler)
} catch (reason) {
rejectHandler(reason)
}
}
//then 方法 同步时获取value 值或 reason 原因 异步 时存储成功或者失败时的回调函数
// then方法有两个参数第一个参数是成功时候的回调函数,第二个是失败时候的回调函数
then(fn1, fn2) {
//先对fn1 fn2 判断一下时不时传进来的是一个函数
fn1 = typeof fn1 === 'function' ? fn1 : (v) => v;
fn2 = typeof fn2 === 'function' ? fn2 : (r) => r
//执行then方法的时候由于不知道执行者当前的状态时处于那个状态下,顾名思义需要定义三种状态下的所有情况
// 另外就是不管是处于那个状态,返回值必须也是一个实例,这样才能满足链式调用的代码逻辑
if (this.state === 'pending') {
const p = new MyPromise((resolveHandler, rejectHandler) => {
//如果当前状是时pending值的时候 意味着调用then方法时的当前实例处于pending状态,
//此时传进来的回调函数需要先记录存起来,等到状态切换 时候 执行
//定义resolveCallbacks rejectCallbacks 成员变量记录存储
this.resolveCallbacks.push(
() => {
try {
//当前实例调用完新的成功回调函数后,会返回一个新值 作为下一个实例的value值
const newValue = fn1(this.value)
resolveHandler(newValue)
} catch (reason) {
rejectHandler(reason)
}
}
)
this.rejectCallbacks.push(
() => {
try {
//当前实例调用完新的失败回调函数后,会返回一个新原因 作为下一个实例的reason原因
const newReason = fn2(this.reason)
rejectHandler(newReason)
} catch (reason) {
rejectHandler(reason)
}
}
)
})
return p
}
if (this.state === 'fulfilled') {
const p = new MyPromise((resolveHandler, rejectHandler) => {
//采用try catch 方式以防 传进来的成功回调函数有问题导致报错
try {
//当前实例调用完新的成功回调函数后,会返回一个新值 作为下一个实例的value值
const newValue = fn1(this.value)
resolveHandler(newValue)
} catch (reason) {
rejectHandler(reason)
}
})
return p
}
if (this.state === 'rejected') {
const p = new MyPromise((resolveHandler, rejectHandler) => {
//采用try catch 方式以防 传进来的成功回调函数有问题导致报错
try {
//当前实例调用完新的失败回调函数后,会返回一个新原因 作为下一个实例的reason原因
const newReason = fn2(this.reason)
rejectHandler(newReason)
} catch (reason) {
rejectHandler(reason)
}
})
return p
}
}
catch(fn) {
return this.then(null, fn)
}
// 全局静态方法
static resolve = ((value) => {
const p = new MyPromise((resolveHandler, rejectHandler) => {
resolveHandler(value)
})
return p
})
static reject = ((reason) => {
const p = new MyPromise((resolveHandler, rejectHandler) => {
rejectHandler(reason)
})
return p
})
static all = ((promiseList = []) => {
const pAll = new MyPromise((resolveHandler, rejectHandler) => {
const result = [] //存储所有成功后的回调结果 作为要返回的当前实例的value输出
const length = promiseList.length
let countResult = 0 //用于计数有多少成功回调
promiseList.forEach(p => {
p.then((data) => {
result.push(data)//把传进来的每个promise实例成功回调后的value值存起来
countResult++
//如果计数达到传进来的实例数组的长度,意味着遍历到了最后一项可以返回成功的回调数组
if (countResult === length) {
resolveHandler(result)
}
}).catch((reason) => {
rejectHandler(reason)
})
})
})
return pAll
})
static race = ((promiseList = []) => {
let isResolve = false; //用于标记着第一个成功回调
const pRace = new MyPromise((resolveHandler, rejectHandler) => {
promiseList.forEach((p)=>{
p.then((data)=>{
if(!isResolve){
resolveHandler(data)
isResolve = true
}
}).catch((reason)=>{
rejectHandler(reason)
})
})
})
return pRace
})
}