JavaScript关于异步

95 阅读6分钟

主要用来当作一个个人笔记,分享的意味不大,因此内容描述也是用我自己习惯的方式,不喜勿怪。

实现Promise

// 常量,表示Promise的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class Promise {
// 构造器 - 接收一个任务
    constructor(task) {
        this.state = PENDING // 初始状态
        this.value = undefined // 任务执行成功的结果
        this.reason = undefined // 任务执行失败的原因
        this.onFulfilledCallbacks = [] // 任务执行成功要触发的回调
        this.onRejectedCallbacks = [] // 任务执行失败要触发的回调

// 定义 resolve reject两个方法
        // 调用resolve告诉Promise任务执行成功,做相应操作
        const resolve = value => {
            // 确保状态只能修改一次
            if (this.state === PENDING) {
                this.state = FULFILLED // 状态修改为成功
                this.value = value // 执行成功的结果
                this.onFulfilledCallbacks.forEach(cb => cb(this.value)) // 调用执行成功对应回调
            }
        }
        // 调用rejected告诉Promise任务执行失败,做相应操作
        const reject = reason => {
            if (this.state === PENDING) {
                this.reason = reason // 执行失败的原因
                this.state = REJECTED // 状态修改为失败
                this.onRejectedCallbacks.forEach(cb => cb(this.reason)) // 调用执行失败对应的回调
            }
        }

// 开始执行任务
        try {
            // 执行传入Promise构造函数中的任务
            // 将resolve、reject作为参数传入,通过这两个参数在任务中修改Promise的状态
            task(resolve, reject)
        } catch (e) { // 任务执行失败则调用reject
            reject(e)
        }
    }
    /*
    * 只有当resolve/reject执行完成后 onFulfilled/onRejected 函数才会执行,因此onFulfilled/onRejected
    *   可以理解为订阅者。resolve。reject为订阅者
    * */
    then(onFulfilled, onRejected) {
        // then方法会返回一个promise,连续调用
        const promise = new Promise((resolve, reject) => {
        
// Promise的链式调用可以理解为值传递和异常传递
            // 成功 值传递
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x
                // 失败 异常传递
            onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e }
            /*
            * 因为需要处理异步任务,所以不能将回调压入回调列表中,需要先对回调做一层封装,将 promise2
            *   和 resolve 函数和 onFulfilled 封装在一起,当 onFulfilled 执行时,可以获取它的返回值x
            * */
            if (this.state === PENDING) {
                /*
                * 进入then方法后,如果Promise状态为Pending,将 onFulfilled 和 onRejected 压入执行栈。
                *   订阅者通知后(出发resolve/reject)便会遍历执行回调列表。
                * */
// 将传入的 onFulfilled、onRejected 进行包装并放到回调列表中,供Promise状态改变时调用
                this.onFulfilledCallbacks.push(() => {
                    // 如果传入的 onFulfilled 执行异常,则通过修改Promise状态为失败并抛出异常
                    // 顺里执行则以Promise上一次 返回的结果 作为参数,继续执行下一轮任务
                    try {
                        const x = onFulfilled(this.value)
                        resolve(x)
                    } catch (error) {
                        reject(error)
                    }
                })
                this.onRejectedCallbacks.push(() => {
                    // 如果传入的 onRejected 执行异常,则通过修改Promise状态为失败并抛出异常
                    // 顺里执行则以Promise上一次 抛出的错误 作为参数,继续执行下一轮任务,与成功同理
                    try {
                        const r = onRejected(this.reason)
                        resolve(r)
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            if (this.state === FULFILLED) {
                // 成功状态执行成功的回调
                try {
                    const x = onFulfilled(this.value)
                    resolve(x)
                } catch (error) {
                    reject(error)
                }
            }
            if (this.state === REJECTED) {
                // 失败状态执行失败的回调
                try {
                    const r = onRejected(this.reason)
                    resolve(r)
                } catch (error) {
                    reject(error)
                }
            }
        })
        return promise
    }
}

生成器 - 迭代器

function * generator (num) {
    const r1 = yield num * 2
    console.log(r1, 'r1')
    const r2 = yield r1 * 2
    console.log(r2, 'r2')
    const r3 = yield r2 * 2
    console.log(r3, 'r3')
    const r4 = yield r3 * 2
    console.log(r4, 'r4')
    return r4
}

const iterator = generator(5)

let res = {}
for (; !res.done;) {
    res = iterator.next(res.value)
    console.log(res, 'res')
}

输出结果:
{ value: 10, done: false } res
10 r1
{ value: 20, done: false } res
20 r2 { value: 40, done: false } res
40 r3
{ value: 80, done: false } res
80 r4
{ value: 80, done: true } res

调用generator生成器函数返回一个迭代器,调用生成器时传入一个数字,这个数字决定迭代器第一次回送的值。
当第一次调用迭代器的next时,回送第一个值,即5*2,实际返回一个迭代结果对象{ value: 10, done: false },第二次调用next时,将第一次回送的值作为迭代器的next方法的参数传入,在生成器内部通过yield关键字的返回值,可以接收到传入的这个值,将它作为下一次回送的基础值。

执行流程:

next第几次调用传入参数回送的值yield关键字的返回值
1undefined1010
2102020
3204040
4408080

第n次执行yield的返回值,是由第n + 1次调用迭代器的next方法传入的参数决定。
迭代器第一次回送的值,可以写死,也可以由调用生成器传入的参数决定。上面的generator就是由调用生成器传入的参数决定。

模拟async - await

// 模拟异步任务
function asyncTask(num) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(num * 3)
        }, 1000)
    })
}
// 模拟同步任务
function syncTask(num) {
    return num * 3
}

执行该生成器会得到一个迭代器,它第一次回送的值由调用生成器函数时传入的参数决定。
第一次:next() 对应 yield asyncTask(initNum)
第二次:next() 对应 yield syncTask(v1) 第三次:next() 对应 yield asyncTask(v2)

function* generator(initNum) {
    const v1 = yield asyncTask(initNum) // 异步
    console.log(v1, 'v1')
    const v2 = yield syncTask(v1) // 同步
    console.log(v2, 'v2')
    const v3 = yield asyncTask(v2) // 异步
    console.log(v3, 'v3')
    return v3 // 同步
}

调用生成器函数可以得到一个迭代器对象
第一次调用迭代器的next方法会执行yield asynTask(2),有了结果会被回送出去,即next方法的返回值
第二次调用next方法的时候,传入第一次调用next方法的返回值,传入的参数将作为第一次执行yield xxx的返回值
相当于在生成器函数内部拿到第一次回送出去的值(第一次执行yield的返回值)

asyncToGenerator 接收一个生成器函数,返回一个函数Fn,Fn返回一个Promise对象
Promise对象内部:只说明正常执行的情况

  • 1.调用传入 asyncToGenerator 函数的参数(调用生成器函数时,给它传入调用Fn时传入的参数),得到一个迭代器

对应第一次循环:const iterator = generatorFn.apply(this, arguments)

  • 2.创建一个 next 函数,接收参数key(该key对应的是迭代器对象上的方法名)

对应第一次循环:function next(key = 'next', arg)

  • 3.首先调用迭代器的next方法,返回一个迭代结果对象,默认该迭代结果对象中的value是一个Promise对象

const { value, done } = iteratorkey

  • 4.判断该迭代结果对象的 done 属性是否为true,如果是说明已经执行完毕

if (done) { resolve(value) }

  • 5.否则调用 迭代结果对象的 then方法,调用前为其包装一层Promise兼容同步任务

Promise.resolve(value).then()

  • 6.then方法执行完毕后继续执行next,即再一次调用迭代结果对象的next方法,并将上一次任务的结果作为参数传入

Promise.resolve(value).then(res => next('next', res), err => next('throw', err))

function asyncToGenerator(generatorFn) {
    return function Fn () {
        return new Promise((resolve, reject) => {
            // 调用生成器函数获取对应的迭代器对象
            const iterator = generatorFn.apply(this, arguments)

            function next(key = 'next', arg) {
                let res
                try {
                    // 任务的执行结果 - { done: Boolean, value: Promise[pending] } | { done: Boolean, value: Any }
                    res = iterator[key](arg)
                    console.log(res, '迭代结果对象')
                } catch (error) {
                    reject(error)
                }
                // value就是yield后的表达式,一个promise对象
                const { value, done } = res
                if (done) { // 没有任务了
                    resolve(value) // 返回最终结果
                } else {
                    // 包装一层Promise,兼容同步任务
                    Promise.resolve(value).then(res => next('next', res), err => next('throw', err))
                }
            }
            next('next')
        })
    }
}

上面代码中next是整体功能的核心,next('next')用来启动程序。

use

调用 asyncToGenerator 返回一个异步函数

const asyncFn = asyncToGenerator(generator)
asyncFn(3).then(res => console.log(res))