温故而知新(2)

85 阅读5分钟

JS 异步

  1. event loop
  2. promise
  3. async/await
  4. 微任务/宏任务

请描述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 有哪三种状态 如何变化

  1. pending
  2. resolved
  3. 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
    })
}