在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#示例
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 是可以选择参数的单传或者不传,都不会影响执行。
例如下面这种
标签:异步,顺序,console,value,js,MyPromise,resolve,promise,reject From: https://www.cnblogs.com/orangehome/p/16968024.html