事件循环&Promise
事件循环(eventloop)
前端事件循环指的是js执行的机制,概括一下:
javascript代码都可以当作任务,其中划分宏任务和微任务,在执行栈中每次处理完宏任务都会去清空一下微任务的队列。
宏任务
setTimeout、setInterval、I/O 操作、DOM 事件等;
微任务
Promise.then ES6、、Object.observer、MutationObserver、process.nextTick、queueMicrotask创建的微任务等;
整体来讲js在从上往下执行的时候遇到的微任务会被放在微任务队列,当前宏任务执行完毕之后就会将微任务推入执行栈执行,又可能会产生新的宏微任务,当然每次微任务被清空时说明本次事件循环结束
图例:浏览器的任务执行流程
Promise
Promise是ES6新增特性,用于解决异步回调或者说回调地狱。
promise有三种状态pendding、rejected、resolved,一个 promise 对象状态只能改变一次。
优点:
- 解决回调链
- 可链式调用
- 有all、race、any等api便于异步任务资源整合
缺点:
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部;
- 当前pendding状态无法知道进展;
- 无法取消;
简单实现promise(面试重点)
整理promise的要点
- promise接收一个函数,函数中有两个参数resolve和reject函数,所以需要定义resolve、reject函数并且接收对应的参数;
- promise有三个状态pendding、rejected、fulfalled,默认pendding状态,可通过resolve和reject函数改变状态;
- then函数
函数接收两个参数一个是成功的回调一个是失败的回调,必须是函数,状态变为fulfalled/rejected的时候去调用对应的函数
function PromiseF (callback){
this.status = 'pendding'
this.value = null //储存resolve传递的值
this.reason = null //储存reject传递的值
const resolve = (value)=>{
if(this.status == 'pendding'){
this.value = value
this.status = 'fulfalled'
}
}
const reject = (reason)=>{
if(this.status == 'pendding'){
this.reason = reason
this.status = 'rejected'
}
}
callback(resolve,reject)
}
PromiseF.prototype.then = function (onResolved,onRejected){
onResolved = typeof onResolved == 'function'?onResolved :(data)=>data
onRejected = typeof onRejected == 'function'?onRejected :(err)=>{throw err}
if(this.status == 'fulfalled'){
onResolved(this.value)
}
if(this.status == 'rejected'){
onResolved(this.reason)
}
}
简单实现promise但是还无法链式调用无法支持异步
promise进阶 实现支持异步和链式回调
- 支持异步需要resolve / reject 执行了之后,再执行 onfulfilled 和 onrejected;
- onfulfilled 和 onjected 应该是微任务;
- 链式回调的实现说明调用then函数之后的返回值也是一个promise,或者说函数的返回值应该是promise的一个新的resolve;
那么利用创建微任务(queueMicrotask)的api去执行对应的函数,创建onFulfalledArr、onRejectedArr去收集对应的函数回调,然后循环数组去执行函数
function PromiseF (callback){
this.status = 'pendding'
this.value = null //储存resolve传递的值
this.reason = null //储存reject传递的值
this.onFulfalledArr = []
this.onRejectedArr = []
const resolve = (value)=>{
queueMicrotask(()=>{
console.log('微任务resolve')
if(this.status == 'pendding'){
this.value = value
this.status = 'fulfalled'
this.onFulfalledArr.forEach(fn=>{
fn(value)
})
}
})
}
const reject = (reason)=>{
queueMicrotask(()=>{
console.log('微任务reject')
if(this.status == 'pendding'){
this.reason = reason
this.status = 'rejected'
this.onRejectedArr.forEach(fn=>{
fn(reason)
})
}
})
}
callback(resolve,reject)
}
PromiseF.prototype.then = function (onResolved,onRejected){
onResolved = typeof onResolved == 'function'?onResolved :(data)=>data
onRejected = typeof onRejected == 'function'?onRejected :(err)=>{throw err}
let promiseBack;
if(this.status == 'fulfalled'){
return promiseBack = new PromiseF((resolve,reject)=>{
queueMicrotask(() => {
console.log('微任务.then直接执行')
try {
const result = onResolved(this.value)
resolve(result)
} catch (error) {
reject(error)
}
})
})
}
if(this.status == 'rejected'){
return promiseBack = new PromiseF((resolve,reject)=>{
queueMicrotask(() => {
console.log('微任务.then直接执行')
try {
const reason = onResolved(this.reason)
reject(reason)
} catch (error) {
reject(error)
}
})
})
}
if(this.status == 'pendding'){
return promiseBack = new PromiseF((resolve,reject)=>{
console.log('微任务.then收集依赖')
try {
this.onFulfalledArr.push(()=>{
const result = onResolved(this.value)
resolve(result)
})
} catch (error) {
reject(error)
}
try {
this.onRejectedArr.push(()=>{
const reason = onRejected(this.reason)
reject(reason)
})
} catch (error) {
reject(error)
}
})
}
}
promise的另一个重点链式调用的resolvePromise 规范
这里是直接去拿的文档,知道有这么回事儿就好,面试一般不会深追这里,逻辑太复杂了
- 如果 promise2 和 x 相等,那么 reject error;
- 如果 promise2 是一个 promise
- 如果 x 是一个pending 状态,那么 promise2 必须要再 pending, 直到 x 变成 fulfilled / rejected
- 如果 x 被 fulfilled, fulfill promise with the same value
- 如果 x 被 rejected, reject promise with the same reason
- 如果 x 是一个 object 或者 function
- Let thenable = x.then
- 如果 x.then 这一步出错,那么 reject promise with e as the reason
- 如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromiseFn)
- resolvePromiseFn 的入参是y, 执行 resolvePromise(promise2, y, resolve, reject)
- rejectPromiseFn 的入参是 r, reject promise with r
- 如果 resolvePromiseFn 和 rejectPromiseFn 都调用了,那么第一个调用优先,后面的忽略
- 如果调用then 抛出异常
- 如果 resolvePromise 或 rejectPromise 已经被调用,可以忽略
- 如果 then 不是一个 function, fulfill promise with x