主要用来当作一个个人笔记,分享的意味不大,因此内容描述也是用我自己习惯的方式,不喜勿怪。
实现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关键字的返回值 |
|---|---|---|---|
| 1 | undefined | 10 | 10 |
| 2 | 10 | 20 | 20 |
| 3 | 20 | 40 | 40 |
| 4 | 40 | 80 | 80 |
第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))