在js中,任务可分为两种,同步任务和异步任务。
(1) 同步任务
又叫 非耗时任务,指的是在主线程排队执行的那些任务
只有前一个任务执行完毕,才能执行后一个任务
(2) 异步任务
又叫 耗时任务,异步任务由JavaScript委托给宿主环境进行执行
当异步任务执行完成后,会通知JavaScript主线程执行异步任务的回调函数
当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务
现有a、b和c三个任务,如果其为同步任务,可以很简单地顺序执行,但如果其为异步任务,该如何顺序执行呢?
一、回调函数
function thing(thingName, callback) {
setTimeout(() => {
console.log(`执行${thingName}任务`)
typeof callback === 'function' && callback()
}, 1000)
}
// 执行a任务
// 执行b任务
// 执行c任务
thing('a', () => {
thing('b', () => {
thing('c')
})
})
优点:简单、方便、实用
缺点:回调函数层层嵌套,不易于阅读和维护,形成回调地狱。
二、promise
1. 使用方式
基本使用
new Promise ((resolve, reject) => {
// 执行代码
}).then(() => {
// 期约兑现
}).catch(() => {
// 期约拒绝
})
详细使用戳这里
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise#%E7%A4%BA%E4%BE%8B
2. 异步任务顺序执行
function thing(thingName) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`执行${thingName}任务`)
resolve()
}, 1000)
})
}
// 执行a任务
// 执行b任务
// 执行c任务
thing('a')
.then(() => thing('b'))
.then(() => thing('c'))
3. 实现原理
那么如何实现一个promise呢?实现promise之前,先分析下promise的结构
-
promise存在三个状态,pending 待定, fulfilled 兑现, rejected 拒绝,因此需要
(1)定义三个常量 PENDING、FULFILLED、REJECTED 对应三种状态
(2)定义 status 表示期约当前状态
(3)定义 value 表示已兑现期约值、定义 reason 表示已拒绝期约原因
-
在调用promise的实例函数时,我们传入了一个执行器,执行器接收两个函数,其作用分别为将待定期约转化为已兑现期约与已拒绝期约。因此需要定义两个函数 resolve 和 reject
-
已兑现期约、已拒绝期约处理函数 then
已拒绝期约处理函数 catch
最终执行函数 finally
-
静态函数 resolve、reject、 all 及 race 的实现
代码实现基于以下链接调整
https://juejin.cn/post/6945319439772434469#heading-30
3.1 promise简易实现
// MyPromise.js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
// 期约状态, 初始值为pending
status = PENDING
// 已兑现期约值
value = null
// 已拒绝期约原因
reason = null
constructor(executor){
executor(this.resolve, this.reject)
}
// 将待定期约转化为已兑现期约
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
}
}
// 将待定期约转化为已拒绝期约
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
}
}
then (onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
} else if (this.status === REJECTED) {
onRejected(this.reason)
}
}
}
module.exports = MyPromise
同目录下新建 test.js 文件用于测试
// test.js
// 引入我们的 MyPromise.js
const MyPromise = require('./MyPromise')
const promise1 = new MyPromise((resolve, reject) => {
resolve('resolve')
})
const promise2 = new MyPromise((resolve, reject) => {
reject('reject')
})
promise1.then(value => {
console.log('promise1 then', value)
}, reason => {
console.log('promise1 catch', reason)
})
promise2.then(value => {
console.log('promise2 then', value)
}, reason => {
console.log('promise2 catch', reason)
})
// 执行结果
// promise1 then resolve
// promise2 catch reject
3.2 加入异步逻辑
继续测试,发现异步执行resolve函数时,会存在问题。因此,我们需对异步逻辑进行处理。
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => resolve('resolve'), 0)
})
promise.then(value => {
console.log('promise then', value)
}, reason => {
console.log('promise catch', reason)
})
// 期望输出 promise then resolve
// 实际无输出
(1) 缓存兑现与拒绝回调
// 存储兑现回调函数
onFulfilledCallback = null
// 存储拒绝回调函数
onRejectedCallback = null
(2) then 方法中新增待定期约处理
then (onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else if (this.status === PENDING) {
this.onFulfilledCallback = onFulfilled
this.onRejectedCallback = onRejected
}
}
(3) resolve 与 reject 中调用回调函数
// 将待定期约转化为已兑现期约
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
// 兑现回调函数存在则执行
this.onFulfilledCallback && this.onFulfilledCallback(value)
}
}
// 将待定期约转化为已拒绝期约
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
// 拒绝回调函数存在则执行
this.onRejectedCallback && this.onRejectedCallback(reason)
}
}
使用以下代码再次验证,异步问题解决。
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => resolve('resolve'), 0)
})
promise.then(value => {
console.log('promise then', value)
}, reason => {
console.log('promise catch', reason)
})
// 执行结果: promise then resolve
3.3 实现 then 方法多次调用添加多个处理函数
Promise支持添加多个处理函数,来测试下自定义Promise是否满足。
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000)
})
promise.then(value => {
console.log(1)
console.log('resolve', value)
})
promise.then(value => {
console.log(2)
console.log('resolve', value)
})
promise.then(value => {
console.log(3)
console.log('resolve', value)
})
// 3
// resolve success
经测试,自定义Promise并不能添加多个处理函数,继续修改。
(1) 新增兑现与拒绝回调数组
// 存储兑现回调函数数组
onFulfilledCallbacks = []
// 存储拒绝回调函数数组
onRejectedCallbacks = []
(2) then方法中存储回调
then (onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
(3) resolve 与 reject 中循环调用回调函数
// 将待定期约转化为已兑现期约
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
// 兑现回调函数存在则执行
while (this.onFulfilledCallbacks.length) {
// Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
this.onFulfilledCallbacks.shift()(value)
}
}
}
// 将待定期约转化为已拒绝期约
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
// 拒绝回调函数存在则执行
while (this.onRejectedCallbacks.length) {
// Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
this.onRejectedCallbacks.shift()(reason)
}
}
}
再次测试,问题解决。
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000)
})
promise.then(value => {
console.log(1)
console.log('resolve', value)
})
promise.then(value => {
console.log(2)
console.log('resolve', value)
})
promise.then(value => {
console.log(3)
console.log('resolve', value)
})
// 1
// resolve success
// 2
// resolve success
// 3
// resolve success
3.4 实现then方法的链式调用
promise是支持链式调用的,我们用自定义的promise来测试下,看是否满足。
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
// 目前这里只处理同步的问题
resolve('success')
})
function other () {
return new MyPromise((resolve, reject) =>{
resolve('other')
})
}
promise.then(value => {
console.log(1)
console.log('resolve', value)
return other()
}).then(value => {
console.log(2)
console.log('resolve', value)
// TypeError: Cannot read property 'then' of undefined
可以看到,第一个then函数的返回值为undefined,不能链式调用。继续修改
class MyPromise {
......
then (onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
const x = onFulfilled(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(x, resolve, reject)
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
})
return promise2
}
}
function resolvePromise(x, resolve, reject) {
// 判断x是不是 MyPromise 实例对象
if(x instanceof MyPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
// x.then(value => resolve(value), reason => reject(reason))
// 简化之后
x.then(resolve, reject)
} else{
// 普通值
resolve(x)
}
}
测试,完成链式调用。
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
// 目前这里只处理同步的问题
resolve('success')
})
function other () {
return new MyPromise((resolve, reject) =>{
resolve('other')
})
}
promise.then(value => {
console.log(1)
console.log('resolve', value)
return other()
}).then(value => {
console.log(2)
console.log('resolve', value)
})
// 1
// resolve success
// 2
// resolve other
3.5 then 方法链式调用识别 Promise 是否返回自己
如果 then 方法返回的是自己的 Promise 对象,则会发生循环调用,这个时候程序会报错,原生Promise测试如下
// test.js
const promise = new Promise((resolve, reject) => {
resolve(100)
})
const p1 = promise.then(value => {
console.log(value)
return p1
})
// 100
// UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #<Promise>
在 MyPromise 实现一下
// MyPromise.js
class MyPromise {
......
then (onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
const x = onFulfilled(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
})
return promise2
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 如果相等了,说明return的是自己,抛出类型错误并返回
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 判断x是不是 MyPromise 实例对象
if(x instanceof MyPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
// x.then(value => resolve(value), reason => reject(reason))
// 简化之后
x.then(resolve, reject)
} else{
// 普通值
resolve(x)
}
}
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
resolve('success')
})
const p1 = promise.then(value => {
console.log('resolve', value)
return p1
})
运行一下,结果报错了。从错误提示可以看出,我们必须要等 p1 完成初始化。这里就需要创建一个异步函数去等待 p1 完成初始化,此处使用微任务 --> queueMicrotask
修改并执行
// MyPromise.js
class MyPromise {
......
then (onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
queueMicrotask(() => {
const x = onFulfilled(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
})
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
})
return promise2
}
}
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
resolve('success')
})
const p1 = promise.then(value => {
console.log('resolve1', value)
return p1
})
// 运行的时候会走reject
p1.then(value => {
console.log('resolve2', value)
}, reason => {
console.log('reject')
console.log(reason.message)
})
// 执行结果
// resolve1 success
// reject
// Chaining cycle detected for promise #<Promise>
3.6 错误处理
(1) 捕获执行器错误
// MyPromise.js
class MyPromise {
......
constructor(executor){
try {
executor(this.resolve, this.reject)
} catch (error) {
// 如果有错误,就直接执行 reject
this.reject(error)
}
}
}
测试一下
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
// resolve('success')
throw new Error('执行器错误')
})
// 2
// 执行器错误
promise.then(value => {
console.log(1)
console.log('resolve', value)
}, reason => {
console.log(2)
console.log(reason.message)
})
测试通过。
(2) then 执行的时错误捕获
// MyPromise.js
class MyPromise {
......
then (onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
try {
queueMicrotask(() => {
const x = onFulfilled(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
})
} catch (error) {
reject(error)
}
} else if (this.status === REJECTED) {
onRejected(this.reason)
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
})
return promise2
}
}
测试一下
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
resolve('success')
})
// 1
// resolve success
// 4
// then error
promise.then(value => {
console.log(1)
console.log('resolve', value)
throw new Error('then error')
}, reason => {
console.log(2)
console.log(reason.message)
}).then(value => {
console.log(3)
console.log(value)
}, reason => {
console.log(4)
console.log(reason.message)
})
测试通过。
3.7 rejected及pending状态改造
- 增加异步状态下的链式调用
- 增加回调函数执行结果的判断
- 增加识别 Promise 是否返回自己
- 增加错误捕获
// MyPromise.js
class MyPromise {
......
then (onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
const rejectedMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = onRejected(this.reason)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.status === FULFILLED) {
fulfilledMicrotask()
} else if (this.status === REJECTED) {
rejectedMicrotask()
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(fulfilledMicrotask)
this.onRejectedCallbacks.push(rejectedMicrotask)
}
})
return promise2
}
}
3.8 then 中的参数变为可选
上面我们处理 then 方法的时候都是默认传入 onFulfilled、onRejected 两个回调函数,但是实际上原生 Promise 是可以选择参数的单传或者不传,都不会影响执行。
例如下面这种 👇
// test.js
const promise = new Promise((resolve, reject) => {
resolve(100)
})
promise
.then()
.then()
.then()
.then(value => console.log(value))
// 输出 100
所以我们需要对 then 方法做一点小小的调整
// MyPromise.js
class MyPromise {
......
then (onFulfilled, onRejected) {
// 如果不传,就使用默认函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const promise2 = new MyPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
const rejectedMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = onRejected(this.reason)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.status === FULFILLED) {
fulfilledMicrotask()
} else if (this.status === REJECTED) {
rejectedMicrotask()
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(fulfilledMicrotask)
this.onRejectedCallbacks.push(rejectedMicrotask)
}
})
return promise2
}
}
改造完自然是需要验证一下的
先看情况一:resolve 之后
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
resolve('succ')
})
promise.then().then().then(value => console.log(value))
// 打印 succ
再看情况二:reject 之后
// test.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
reject('err')
})
promise.then().then().then(value => console.log(value), reason => console.log(reason))
// 打印 err
3.9 catch、finally函数实现
(1) catch 其实是个语法糖,可以通过then函数传入第二个参数实现
(2) finally 的特点如下
无论promise成功或失败,finally方法都会执行接收到的回调函数,并返回一个promise实例:
a. 如果回调函数执行出错,将以抛出的错误,拒绝新的promise;
b. 否则,新返回的promise会沿用旧promise的决议值进行决议。
// MyPromise.js
class MyPromise {
......
catch (onRejected) {
return this.then(null, onRejected)
}
// 无论promise成功或失败,finally方法都会执行接收到的回调函数,并返回一个promise实例:
// 1. 如果回调函数执行出错,将以抛出的错误,拒绝新的promise;
// 2. 否则,新返回的promise会沿用旧promise的决议值进行决议。
finally (callback) {
return this.then(
(data) => {
callback()
return data
},
(error) => {
callback()
throw error
}
)
}
}
3.10 静态函数实现
(1) resolve
static resolve (parameter) {
// 如果传入 MyPromise 就直接返回
if (parameter instanceof MyPromise) {
return parameter
}
return new MyPromise(resolve => {
resolve(parameter)
})
}
(2) reject
// MyPromise.js
class MyPromise {
......
static reject (reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
}
(3) all
// MyPromise.js
class MyPromise {
......
static all (promiseArray) {
let result = []
let success = 0
return new Promise((resolve, reject) => {
promiseArray.forEach((promise, i) => {
MyPromise.resolve(promise)
.then(res => {
result[i] = res
success++
// 全部成功
if (success === promiseArray.length) {
resolve(result)
}
}, err => reject(err))
})
})
}
}
(4) race
// MyPromise.js
class MyPromise {
......
static race (promiseArray) {
return new Promise((resolve, reject) => {
promiseArray.forEach((promise) => {
MyPromise.resolve(promise)
.then(res => {
resolve(res)
}, err => reject(err))
})
})
}
}
验证一下,木有问题
// test.js
const MyPromise = require('./MyPromise')
// resolve success
MyPromise
.resolve('success')
.then((res) => {
console.log('resolve', res)
})
// reject fail
MyPromise
.reject('fail')
.then(() => {}, err => {
console.log('reject', err)
})
// all [ 'a', 'b', 'c' ]
MyPromise
.all([thing('a', 1000), thing('b', 2000), thing('c', 3000)])
.then(res => console.log('all', res))
// race a
MyPromise
.race([thing('a', 1000), thing('b', 2000), thing('c', 3000)])
.then(res => console.log('race', res))
function thing (thingName, timeout) {
return new MyPromise(
resolve => setTimeout(() => {
resolve(thingName)
}, timeout)
)
}
3.11 最终代码
// MyPromise.js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
// 期约状态, 初始值为pending
status = PENDING
// 已兑现期约值
value = null
// 已拒绝期约原因
reason = null
// 存储兑现回调函数数组
onFulfilledCallbacks = []
// 存储拒绝回调函数数组
onRejectedCallbacks = []
constructor(executor){
try {
executor(this.resolve, this.reject)
} catch (error) {
// 如果有错误,就直接执行 reject
this.reject(error)
}
}
// resolve 静态方法
static resolve (parameter) {
// 如果传入 MyPromise 就直接返回
if (parameter instanceof MyPromise) {
return parameter
}
return new MyPromise(resolve => {
resolve(parameter)
})
}
// reject 静态方法
static reject (reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
static all (promiseArray) {
let result = []
let success = 0
return new Promise((resolve, reject) => {
promiseArray.forEach((promise, i) => {
MyPromise.resolve(promise)
.then(res => {
result[i] = res
success++
// 全部成功
if (success === promiseArray.length) {
resolve(result)
}
}, err => reject(err))
})
})
}
static race (promiseArray) {
return new Promise((resolve, reject) => {
promiseArray.forEach((promise) => {
MyPromise.resolve(promise)
.then(res => {
resolve(res)
}, err => reject(err))
})
})
}
// 将待定期约转化为已兑现期约
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
// 兑现回调函数存在则执行
while (this.onFulfilledCallbacks.length) {
// Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
this.onFulfilledCallbacks.shift()(value)
}
}
}
// 将待定期约转化为已拒绝期约
reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
// 拒绝回调函数存在则执行
while (this.onRejectedCallbacks.length) {
// Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
this.onRejectedCallbacks.shift()(reason)
}
}
}
then (onFulfilled, onRejected) {
// 如果不传,就使用默认函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const promise2 = new MyPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
const rejectedMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = onRejected(this.reason)
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.status === FULFILLED) {
fulfilledMicrotask()
} else if (this.status === REJECTED) {
rejectedMicrotask()
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(fulfilledMicrotask)
this.onRejectedCallbacks.push(rejectedMicrotask)
}
})
return promise2
}
catch (onRejected) {
return this.then(undefined, onRejected)
}
finally (finalFunc) {
return this.then(finalFunc, finalFunc)
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 如果相等了,说明return的是自己,抛出类型错误并返回
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 判断x是不是 MyPromise 实例对象
if(x instanceof MyPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
// x.then(value => resolve(value), reason => reject(reason))
// 简化之后
x.then(resolve, reject)
} else{
// 普通值
resolve(x)
}
}
module.exports = MyPromise
4. Promise A+ 测试
4.1 测试
检验一份手写 Promise 靠不靠谱,通过 Promise A+ 规范自然是基本要求,这里我们可以借助 promises-aplus-tests 来检测我们的代码是否符合规范。
(1) 新建test文件夹
(2) 命令行输入以下代码快速初始化
npm init -y
(3) 安装包
npm install promises-aplus-tests -D
(4) 将 MyPromise.js 拷贝至test文件夹,并添加 deferred 函数
// MyPromise.js
......
MyPromise.deferred = function () {
const result = {}
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve
result.reject = reject
})
return result
}
module.exports = MyPromise
(5) 配置启动命令
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "promises-aplus-tests MyPromise"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"promises-aplus-tests": "^2.1.2"
}
}
(6) 命令行输入以下代码执行
node run test
4.2 修改
执行结果如图
可以看出是2.3.x的问题,翻看了一下 Promise A+ 规范,找到以下信息。
Promise A+ 规范链接:https://promisesaplus.com/#point-59
翻译如下
2.3.3 否则如果 x 是一个对象或函数,
2.3.3.1 将x.then赋值给then
2.3.3.2 如果取 x.then 的值时抛出错误 e,则以 e为由拒绝 promise。
2.3.3.3 如果then是一个函数,执行并将其this指向x,第一个入参为resolvePromise,第二个入参为rejectPromise
2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
2.3.3.3.2 如果 rejectPromise以r 为参数被调用,则以r为由拒绝promise
2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
2.3.3.3.4 如果then执行时抛出异常
2.3.3.3.4.1 如果resolvePromise 或 rejectPromise 被调用,则忽略它
2.3.3.3.4.2 否则,以e为由拒绝promise
2.3.3.4 如果then不是一个函数,则以x为值兑现promise
2.3.4 如果x不是一个对象或函数,则以x为值兑现promise
代码修改如下
function resolvePromise(promise, x, resolve, reject) {
// 如果相等了,说明return的是自己,抛出类型错误并返回
if (promise === x) {
return reject(new TypeError('The promise and the return value are the same'))
}
if (typeof x === 'object' || typeof x === 'function') {
// x 为 null 直接返回,走后面的逻辑会报错
if (x === null) {
return resolve(x)
}
let then
try {
// 把 x.then 赋值给 then
then = x.then
} catch (error) {
// 如果取 x.then 的值时抛出错误 error ,则以 error 为据因拒绝 promise
return reject(error)
}
// 如果 then 是函数
if (typeof then === 'function') {
let called = false
try {
then.call(
x, // this 指向 x
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
// 实现这条需要前面加一个变量 called
if (called) return
called = true
resolvePromise(promise, y, resolve, reject)
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r => {
if (called) return
called = true
reject(r)
})
} catch (error) {
// 如果调用 then 方法抛出了异常 error:
// 如果 resolvePromise 或 rejectPromise 已经被调用,直接返回
if (called) return
// 否则以 error 为据因拒绝 promise
reject(error)
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x)
}
} else {
// 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x)
}
}
再次执行,完美解决。
三、generator
generator函数,即生成器函数,是一个状态机,封装了多个内部状态。执行一个generator,会返回一个迭代器对象,通过迭代器对象,可以遍历generator函数内部的每个状态。因此,generator函数可以看做是一个迭代器生成器。
1. 基本使用
(1) generator基本形式
generator 函数是在 function 和函数名之间添加 *
来定义的。yield关键字可以让生成器停止和开始执行,生成器函数在遇到yield关键字之前会正常执行。遇到这个关键字之后,执行会停止,函数作用域的状态会被保留。停止执行的生成器函数可通过next()方法来恢复执行。
function *foo() {
yield 1
yield 2
yield 3
return 4
}
(2) 执行foo得到一个迭代器
const iterator = foo()
(3) 迭代器遍历
可以通过常用迭代器遍历方法如for-of来遍历迭代器
// 1
// 2
// 3
for(let item of iterator){
console.log(item)
}
也可以手动进行遍历
// 1
// 2
// 3
let item = iterator.next()
while (!item.done) {
console.log(item.value)
item = iterator.next()
}
2. 异步任务顺序执行
function thing(thingName) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`执行${thingName}任务`)
resolve()
}, 1000)
})
}
function *generator() {
yield thing('a')
yield thing('b')
yield thing('c')
}
function dothings (generator) {
const thingIterator = generator()
let thingItem
exec()
function exec () {
thingItem = thingIterator.next()
if (!thingItem.done) {
Promise.resolve(thingItem.value).then(exec)
}
}
}
// 执行a任务
// 执行b任务
// 执行c任务
dothings(generator)
四、async/await
1. 实现原理
1)async/await 就是 generator 的语法糖,使得异步操作变得更加方便
2)async 函数就是将 generator 函数的星号(*)替换成 async,将 yield 替换成await
async function thing(thingName) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`执行${thingName}任务`)
resolve()
}, 1000)
})
}
// generator 生成器函数
function myAsync (generator) {
return function () {
const iterator = generator()
let item
return new Promise ((resolve, reject) => exec(resolve, reject))
function exec (resolve, reject) {
item = iterator.next()
if (!item.done) {
Promise.resolve(item.value).then(() => exec(resolve, reject))
} else {
// 返回值处理
if (item.value instanceof Promise) {
item.value.then(resolve, reject)
} else {
resolve(item.value)
}
}
}
}
}
function *generator() {
yield thing('a')
yield thing('b')
yield thing('c')
return 'the end'
}
// 模拟定义一个async函数
const myAsyncFunc = myAsync(generator)
// 执行a任务
// 执行b任务
// 执行c任务
const result = myAsyncFunc()
// 返回为一个promise对象
console.log(result instanceof Promise) // true
// the end
result.then(res => console.log(res))
2. 使用注意事项
1)await 后面的promise对象,当其为一个已拒绝期约时,会阻塞后面代码的执行,举例如下
// the first
// UnhandledPromiseRejectionWarning: undefined
async function test () {
console.log('the first')
await Promise.reject()
console.log('the second')
}
test()
对此,可以使用catch函数或try-catch进行处理
catch函数
// the first
// the second
async function test () {
console.log('the first')
await Promise.reject().catch(() => {})
console.log('the second')
}
test()
try-catch
// the first
// the second
async function test () {
console.log('the first')
try {
await Promise.reject()
} catch (e) {
}
console.log('the second')
}
test()
2)多个await后面的异步操作,如果不存在继发关系,最好让它们同时触发
3)await仅能在async函数中使用
4)async函数返回值为一个promise对象
3. 异步任务顺序执行
a、b、c异步任务顺序执行代码如下:
async function thing(thingName) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`执行${thingName}任务`)
resolve()
}, 1000)
})
}
async function dothings () {
await thing('a')
await thing('b')
await thing('c')
}
// 执行a任务
// 执行b任务
// 执行c任务
dothings()