前言:
针对js的深入理解,作者学习并撰写以下文章,由于理解认知有限难免存在偏差,请大家指正!所有定义来自mdn。
Promise介绍:
对象表示异步操作最终的完成(或失败)以及其结果值.
描述:
一个 Promise
是一个代理,它代表一个在创建 promise 时不一定已知的值。它允许你将处理程序与异步操作的最终成功值或失败原因关联起来。这使得异步方法可以像同步方法一样返回值:异步方法不会立即返回最终值,而是返回一个 promise,以便在将来的某个时间点提供该值。
一个 Promise
必然处于以下几种状态之一:
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled):意味着操作成功完成。
- 已拒绝(rejected):意味着操作失败。
一个待定的 Promise 最终状态可以是已兑现并返回一个值,或者是已拒绝并返回一个原因(错误)。当其中任意一种情况发生时,通过 Promise 的 then
方法串联的处理程序将被调用。如果绑定相应处理程序时 Promise 已经兑现或拒绝,这处理程序将被立即调用,因此在异步操作完成和绑定处理程序之间不存在竞态条件。
如果一个 Promise 已经被兑现或拒绝,即不再处于待定状态,那么则称之为已敲定(settled)。
Promise.resolve:
Promise.resolve()
静态方法将给定的值转换为一个 Promise。如果该值本身就是一个 Promise,那么该 Promise 将被返回;如果该值是一个 thenable 对象,Promise.resolve()
将调用其 then()
方法及其两个回调函数;否则,返回的 Promise 将会以该值兑现。
构造MyPromise函数:
// 构造函数
// 定义类
class MyPromise {
// 添加构造函数
constructor(func){
// 定义resolve和reject方法
const resolve = (value) => {
console.log('resolve执行:',value)
// // 判断状态是否为pending
// if(this.status === 'pending'){
// // 修改状态为fulfilled
// this.status = 'fulfilled'
// // 保存成功的值
// this.value = value
// // 执行成功的回调函数
// this.onFulfilledCallbacks.forEach(fn => fn())
// }
}
const reject = (value) => {
console.log('reject执行:',value)
}
// 初始化状态
func(resolve,reject)
}
}
// 测试代码
const p = new MyPromise((resolve,reject) => {
console.log('执行器函数执行')
resolve('成功')
reject('失败')
})
状态和原因:
含bug代码
<h2>状态及原因</h2>
<script>
// 1、添加原生Promise状态fulfilled/pending/rejected
// 2、添加原生Promise原因resolve/reject
// 3、调整resolve/reject方法
// 4、状态不可逆
// 为了方便和规范起见命名MyPromise的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
// 状态初始化
status = PENDING
result = undefined
// 添加构造函数
constructor(func){
// 定义resolve和reject方法
const resolve = (value) => {
// 修改状态并记录原因
this.status = FULFILLED
this.result = value
}
const reject = (value) => {
// 修改状态并记录原因
this.status = REJECTED
this.result = value
}
// 初始化状态
func(resolve,reject)
}
}
// 测试代码
const p = new MyPromise((resolve,reject) => {
resolve('成功')
reject('失败')
})
在未添加自定义的状态不可逆时代码有误还是会按照js从上向下执行原则最后改变pending从sucees to fail.
接下来我们开始处理不可逆属性:
// 定义resolve和reject方法
const resolve = (result) => {
if(this.status === PENDING){
// 修改状态并记录结果
this.status = FULFILLED
this.result = result
}
}
const reject = (result) => {
if(this.status === PENDING){
// 修改状态并记录结果
this.status = REJECTED
this.result = result
}
// 初始化状态
func(resolve,reject)
}
then方法:
成功失败回调:
tips:被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
// 状态初始化
// 添加状态
status = PENDING
// 添加原因
result = undefined
// 添加构造函数
constructor(func){
// 定义resolve和reject方法
const resolve = (result) => {
if(this.status === PENDING){
// 修改状态并记录结果
this.status = FULFILLED
this.result = result
}
}
const reject = (result) => {
if(this.status === PENDING){
// 修改状态并记录结果
this.status = REJECTED
this.result = result
}
}
// 初始化状态
func(resolve,reject)
}
then(onFulfilled,onRejected){
// 根据mdn知道需要判断回调函数是否为函数,如果不是需要处理
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}
// 判断状态
if(this.status === FULFILLED){
onFulfilled(this.result)
}
else if(this.status === REJECTED){
onRejected(this.result)
}
}
}
// 测试代码
const p = new MyPromise((resolve,reject) => {
// resolve('success')
reject('fail')
})
p.then((res) => {
console.log('成功回调',res)
},(err) => {
console.log('失败抛出错误',err)
})
异步多次调用:
// 定义常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
// 状态初始化
// 添加状态
status = PENDING
// 添加原因
result = undefined
// 私有属性handlers
#handlers = [] //[{onFulfilled,onRejected}...]
// 添加构造函数
constructor(func){
// 定义resolve和reject方法
const resolve = (result) => {
if(this.status === PENDING){
// 修改状态并记录结果
this.status = FULFILLED
this.result = result
// 调用成功回调函数
this.#handlers.forEach(({onFulfilled}) => {
onFulfilled(result)
})
}
}
const reject = (result) => {
if(this.status === PENDING){
// 修改状态并记录结果
this.status = REJECTED
this.result = result
// 调用失败回调函数
this.#handlers.forEach(({onRejected}) => {
onRejected(result)
})
}
}
// 初始化状态
func(resolve,reject)
}
then(onFulfilled,onRejected){
// 根据mdn知道需要判断回调函数是否为函数,如果不是需要处理
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}
// 判断状态
// 同步调用
if(this.status === FULFILLED){
onFulfilled(this.result)
}
else if(this.status === REJECTED){
onRejected(this.result)
}//异步和多次调用
else if(this.status === PENDING){
// 添加回调函数
this.#handlers.push({onFulfilled,onRejected})
}
}
}
// 测试代码
const p = new MyPromise((resolve,reject) => {
setTimeout(() => {
resolve('success')
reject('fail')
},1000)
})
p.then((res) => {
console.log('then1',res)
},(err) => {
console.log('then1',err)
})
p.then((res) => {
console.log('then2',res)
},(err) => {
console.log('then2',err)
})
异步任务:
核心api:
// 异步任务 queueMicrotask
console.log('start')
queueMicrotask(() => {
console.log('queueMicrotask')
})
console.log('end')
// 异步任务 MutationObserver
// 1、创建观察器,并传入回调函数
const obs = new MutationObserver(() => {
console.log('mutationsList')
})
// 2、创建元素,并添加监听
const divNode = document.createElement('div')
// 参数1dom节点,参数2配置对象childList:true表示监听子节点变化
obs.observe(divNode,{childList:true})
// 3、修改元素内容,观察器触发MutationObserver
divNode.innerText = 'hello'
函数封装:
// 封装函数分别使用queueMicrotask和MutationObserver setTimeout实现异步任务
function runAsyncTask(callback){
// 使用queueMicrotask
if(typeof queueMicrotask === 'function')
{
queueMicrotask(callback)
}else if (typeof MutationObserver === 'function'){
// 使用MutationObserver
const observer = new MutationObserver(callback)
const nodeDiv = document.createTextNode('div')
observer.observe(nodeDiv,{childList:true})
node.innerText = 'hello'
}else{
setTimeout((callback) ,0)
}
}
对then中的回调方法增加异步属性
// 使用封装的异步函数
then(onFulfilled,onRejected){
// 根据mdn知道需要判断回调函数是否为函数,如果不是需要处理
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}
// 判断状态
// 同步调用
if(this.status === FULFILLED){
runAsyncTask(() => {
onFulfilled(this.result)
})
}
else if(this.status === REJECTED){
runAsyncTask(() => {
onRejected(this.result)
})
}//异步和多次调用
else if(this.status === PENDING){
// 添加回调函数
this.#handlers.push({
onFulfilled:()=>{
runAsyncTask(() => {
onFulfilled(this.result)
})
}
,
onRejected:()=>{
runAsyncTask(() => {
onRejected(this.result)
})
}
})
}
}
成功实现异步调用
链式编程:
获取返回值:
处理返回值和处理异常:
使用try-catch进行返回值处理和异常捕获,同时在then中内嵌的promise调用和回调函数的使用完成对上一个then的返回值的捕获和返回。
// 1、返回新的promise对象 传入的函数是立刻调用的
const p2 = new MyPromise((resolve,reject)=>{
// 判断状态
// 同步调用
if(this.status === FULFILLED){
runAsyncTask(() => {
// 2、获取返回值
try {
const x = onFulfilled(this.result)
// 2.1处理返回值
resolve(x)
} catch (error) {
reject(error)
}
})
}
else if(this.status === REJECTED){
runAsyncTask(() => {
onRejected(this.result)
})
}//异步和多次调用
else if(this.status === PENDING){
// 添加回调函数
this.#handlers.push({
onFulfilled:()=>{
runAsyncTask(() => {
onFulfilled(this.result)
})
}
,
onRejected:()=>{
runAsyncTask(() => {
onRejected(this.result)
})
}
})
}
})
return p2
处理返回promise:
tips:instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
x为返回的promise实例
runAsyncTask(() => {
// 2、获取返回值
try {
const x = onFulfilled(this.result)
if(x instanceof MyPromise){
x.then(res => resolve(res),err => reject(err))
}
else{resolve(x)}
} catch (error) {
reject(error)
}
})
// 测试代码
// console.log('start')
const p = new MyPromise((resolve,reject) => {
resolve(1)
//reject('fail')
})
p.then((res)=>{
return new MyPromise((resolve,reject) => {
resolve(2)
})
}).then((res) => {
console.log('p2',res)
},(err) => {
console.log('p2',err)
})
获取重复调用:
利用原生Promise报错模仿写自己的Promise返回相同值(Promise)的报错
Chaining cycle detected for promise #<Promise>
// 处理重复引用
const p2 = new MyPromise((resolve,reject)=>{
if(this.status === FULFILLED){
runAsyncTask(() => {
try {
const x = onFulfilled(this.result)
// 判断是否重复
if(x===p2)
{
// 抛出异常Chaining cycle detected for promise #<Promise>
throw new TypeError('Chaining cycle detected for promise #<Promise>')
}
if(x instanceof MyPromise){
x.then(res => resolve(res),err => reject(err))
}
else{resolve(x)}
} catch (error) {
reject(error)
}
})
成功捕获错误!
对于reject同理,同样需要处理返回值和异常、处理返回promise、获取重复调用四个步骤,用于代码需要重复调用,所以我们将其封装成函数以进行复用
// 抽取函数
function resolvePromise(p2,x,resolve,reject){
if(x === p2){
throw new TypeError('Chaining cycle detected for promise #<Promise>')
}
if(x instanceof MyPromise){
x.then(res => resolve(res),err => reject(err))
}else{
resolve(x)
}
}
功能正常!
作为中立状态的pending也必不可少,同样需要处理返回值和异常、处理返回promise、获取重复调用四个步骤。
// 添加回调函数
this.#handlers.push({
onFulfilled:()=>{
runAsyncTask(() => {
// 1、处理异常
try {
// 2、获取返回值
const x = onFulfilled(this.result)
// 3、调用函数
resolvePromise(p2,x,resolve,reject)
} catch (error) {
reject(error)
}
})
}
,
onRejected:()=>{
runAsyncTask(() => {
// 1、处理异常
try {
// 获取返回值
const x = onRejected(this.result)
resolvePromise(p2,x,resolve,reject)
} catch (error) {
reject(error)
}
})
}
})
仍可成功获取。
实例方法:
.catch()
Promise 实例的 catch()
方法用于注册一个在 promise 被拒绝时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 的方法。此方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。
根据文档在then方法中添加已有catch方法,却发现无法调用reject中捕获异常功能,而是在调试throw error使用浏览器报错,故在自己定义的reject方法中使用try catch 捕获错误。
const reject = (result) => {
if(this.status === PENDING){
// 修改状态并记录结果
this.status = REJECTED
this.result = result
// 调用失败回调函数
this.#handlers.forEach(({onRejected}) => {
onRejected(result)
})
}
}
// 处理异常
try {
func(resolve,reject)
} catch (error) {
reject(error)
}
then(onFulfilled,onRejected){
// 根据mdn知道需要判断回调函数是否为函数,如果不是需要处理
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}
// 处理重复引用
const p2 = new MyPromise((resolve,reject)=>{
if(this.status === FULFILLED){
runAsyncTask(() => {
try {
const x = onFulfilled(this.result)
resolvePromise(p2,x,resolve,reject)
// // 判断是否重复
// if(x===p2)
// {
// // 抛出异常Chaining cycle detected for promise #<Promise>
// throw new TypeError('Chaining cycle detected for promise #<Promise>')
// }
// if(x instanceof MyPromise){
// x.then(res => resolve(res),err => reject(err))
// }
// else{resolve(x)}
} catch (error) {
reject(error)
}
})
}
else if(this.status === REJECTED){
runAsyncTask(() => {
try {
const x = onRejected(this.result)
resolvePromise(p2,x,resolve,reject)
} catch (error) {
reject(error)
}
})
}//异步和多次调用
else if(this.status === PENDING){
// 添加回调函数
this.#handlers.push({
onFulfilled:()=>{
runAsyncTask(() => {
// 1、处理异常
try {
// 2、获取返回值
const x = onFulfilled(this.result)
// 3、调用函数
resolvePromise(p2,x,resolve,reject)
} catch (error) {
reject(error)
}
})
}
,
onRejected:()=>{
runAsyncTask(() => {
// 1、处理异常
try {
// 获取返回值
const x = onRejected(this.result)
resolvePromise(p2,x,resolve,reject)
} catch (error) {
reject(error)
}
})
}
})
}
})
return p2
}
catch(onRejected){
return this.then(undefined,onRejected)
}
}
// 测试手写
const p = new MyPromise((resolve,reject) => {
// reject('fail')
throw 'error!'
})
p.then(res=>{
console.log('res:',res)
}).catch(err => {
console.log('err:',err)
})
.finally
Promise 实例的 finally()
方法用于注册一个在 promise 敲定(兑现或拒绝)时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 方法。
无论错误还是成功都不会影响调用.finally时的输出。
finally(onFinally){
return this.then(onFinally,onFinally)
}
静态方法:
.resolve()
Promise.resolve()
静态方法将给定的值转换为一个 Promise。如果该值本身就是一个 Promise,那么该 Promise 将被返回;如果该值是一个 thenable 对象,Promise.resolve()
将调用其 then()
方法及其两个回调函数;否则,返回的 Promise 将会以该值兑现。
// 添加静态方法
static resolve(value){
if(value instanceof MyPromise){
return value
}
return new MyPromise((resolve,reject) => {
resolve(value)
})
}
// 测试手写
MyPromise.resolve(new MyPromise((resolve,reject) => {
//resolve(1)
//reject('fail')
//throw 'error'
})).then(res => {
console.log('res:',res)
},err=>{
console.log('err:',err)
})
MyPromise.resolve('ian').then(res=>{
console.log('res:',res)
})
.reject()
Promise.reject()
静态方法返回一个已拒绝(rejected)的 Promise
对象,拒绝原因为给定的参数。
static reject(value){
return new MyPromise((undefined,reject) => {
reject(value)
})
}
.race()
Promise.race()
静态方法接受一个 promise 可迭代对象作为输入,并返回一个 Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定。
static race(promises){
// 1、返回Promise对象
return new MyPromise((resolve,reject)=>{
// 2、判断是否为数组
if(!Array.isArray(promises)){
return reject(new TypeError('You must pass an array'))
}
// 3、等待第一个敲定
promises.forEach(p=>{
//p.then
MyPromise.resolve(p).then(res => {resolve(res)},err => {reject(err)})
})
})
}
.all()
Promise.all()
静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。
、
static all(promises){
// 1、返回Promise对象
return new MyPromise((resolve,reject)=>{
// 2、判断是否为数组
if(!Array.isArray(promises)){
return reject(new TypeError('Argument is not iterable'))
}
// 3、空数组直接兑现
promises.length === 0 && resolve(promises)
// 4、处理全部兑现
// 4.1、记录结果:使用索引来记录,保证结果的顺序和Promise数组的顺序一致
// 4.2、判断是否全部兑现:通过兑现的次数来判断,保证可以获取道德所有结果
const results = [] // 记录结果
let count = 0 // 记录兑现次数
promises.forEach((p,index)=>{
MyPromise.resolve(p).then(
res => {
results[index] = res
// 判断是否全部兑现
count++
count === promises.length && resolve(results)
},
err => {
// 有一个失败则全部失败
reject(err)
}
)
})
})
}
.allSettled()
Promise.allSettled()
静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。
无论是resolve还是reject都属于promise的pending敲定,使用resolve!
static allSettled(promises){
// 1、返回Promise对象
return new MyPromise((resolve,reject)=>{
// 2、判断是否为数组
if(!Array.isArray(promises)){
return reject(new TypeError('Argument is not iterable'))
}
// 3、空数组直接兑现
promises.length === 0 && resolve(promises)
// 4、处理全部兑现
// 4.1、记录结果:使用索引来记录,保证结果的顺序和Promise数组的顺序一致
const results = [] // 记录结果
let count = 0 // 记录兑现次数
promises.forEach((p,index)=> {
MyPromise.resolve(p).then(
res =>{
// 4.2、处理兑现{status: FULFILLED,value: res}
results[index] = {status: FULFILLED,value: res}
count++
count === promises.length && resolve(results)
},err=>{
// 4.3、处理拒绝{status: REJECTED,reason: err}
results[index] = {status: REJECTED,reason: err}
count++
count === promises.length && resolve(results)
}
)
})
})
}
// 测试手写
const p1 = MyPromise.resolve(1)
const p2 = 2
const p3 = new MyPromise((resolve,reject) => {
setTimeout(() => {
// resolve(3)
reject('fail')
},1000)
})
MyPromise.allSettled([p1,p2,p3]).then(res => {
console.log('res:',res)
},err => {
console.log('err:',err)
})
.any()
Promise.any()
静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝。
AggregateError
对象代表了包装了多个错误对象的单个错误对象。当一个操作需要报告多个错误时,例如 Promise.any(),当传递给它的所有承诺都被拒绝时,就会抛出该错误。
AggregateError
是 Error 的子类。
static any(promises){
// 1、返回Promise对象
return new MyPromise((resolve,reject)=>{
// 2、判断是否为数组
if(!Array.isArray(promises)){
return reject(new TypeError('Argument is not iterable'))
}
// 3、空数组直接拒绝
promises.length === 0 && reject(new AggregateError(promises,'All promises were rejected'))
// 4、处理第一个兑现
// 4.1、记录结果:使用索引来记录,保证结果的顺序和Promise数组的顺序一致
// 4.2、判断是否全部兑现:通过兑现的次数来判断,保证可以获取道德所有结果
const errors = [] // 记录结果
let count = 0 // 记录兑现次数
promises.forEach((p,index)=>{
MyPromise.resolve(p).then(
// 第一个兑现
res => {
resolve(res)
},
err => {
// 全部拒绝
errors[index] = err
count++
count === promises.length && reject(new AggregateError(errors,'All promises were rejected'))
}
)
})
})
}
// 测试手写
const p1 = new MyPromise((resolve,reject) => {
setTimeout(() => {
//resolve(1)
reject(1)
},2000)
})
const p2 = MyPromise.reject(2)
const p3 = new MyPromise((resolve,reject) => {
setTimeout(() => {
//resolve(3)
reject(3)
},1000)
})
// MyPromise.any([]).then(res => {
//MyPromise.any().then(res => {
MyPromise.any([p1,p2,p3]).then(res => {
console.log('res:',res)
},err => {
console.dir(err)
})