前言:
针对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 = PENDINGresult = undefined// 添加构造函数constructor(func){// 定义resolve和reject方法const resolve = (value) => {// 修改状态并记录原因this.status = FULFILLEDthis.result = value}const reject = (value) => {// 修改状态并记录原因this.status = REJECTEDthis.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 = FULFILLEDthis.result = result}}const reject = (result) => {if(this.status === PENDING){// 修改状态并记录结果this.status = REJECTEDthis.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 = FULFILLEDthis.result = result}}const reject = (result) => {if(this.status === PENDING){// 修改状态并记录结果this.status = REJECTEDthis.result = result}}// 初始化状态func(resolve,reject)}then(onFulfilled,onRejected){// 根据mdn知道需要判断回调函数是否为函数,如果不是需要处理onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => valueonRejected = 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 = FULFILLEDthis.result = result// 调用成功回调函数this.#handlers.forEach(({onFulfilled}) => {onFulfilled(result)})}}const reject = (result) => {if(this.status === PENDING){// 修改状态并记录结果this.status = REJECTEDthis.result = result// 调用失败回调函数this.#handlers.forEach(({onRejected}) => {onRejected(result)})}}// 初始化状态func(resolve,reject)}then(onFulfilled,onRejected){// 根据mdn知道需要判断回调函数是否为函数,如果不是需要处理onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => valueonRejected = 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:
// 异步任务 queueMicrotaskconsole.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、修改元素内容,观察器触发MutationObserverdivNode.innerText = 'hello'
函数封装:
// 封装函数分别使用queueMicrotask和MutationObserver setTimeout实现异步任务function runAsyncTask(callback){// 使用queueMicrotaskif(typeof queueMicrotask === 'function'){queueMicrotask(callback)}else if (typeof MutationObserver === 'function'){// 使用MutationObserverconst 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 => valueonRejected = 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 = REJECTEDthis.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 => valueonRejected = 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.thenMyPromise.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 = 2const 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] = errcount++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)})