IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 手写一个迷你版的Promise -> 正文阅读

[JavaScript知识库]手写一个迷你版的Promise

手写一个迷你版的Promise

  • JavaScript 中的 Promise 诞生于 ES2015(ES6),是当下前端开发中特别流行的一种异步操作解决方案,简单实现一个迷你版本帮助深入理解 Promise 的运行原理

芝士点

  • 微任务
  • static
  • 发布-订阅模式

Promise 的声明

const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class JetPromise {
	status = PENDING
	value = undefined
	reason = undefined
	fulfilledCbs = []
	rejectedCbs = []

    constructor(executor) {
		try {
			executor(this.resolve, this.reject)
		} catch (error) {
			this.reject(error)
		}
	}

    resolve = (value) => {
		queueMicrotask(() => {
			if (this.status === PENDING) {
				this.status = FULFILLED
				this.value = value
				this.fulfilledCbs.forEach((fn) => fn())
			}
		})
	}

	reject = (reason) => {
		queueMicrotask(() => {
			if (this.status === PENDING) {
				this.status = REJECTED
				this.reason = reason
				this.rejectedCbs.forEach((fn) => fn())
			}
		})
	}
}
  • 构造一个 JetPromise类,需要传入一个函数作为执行器,在创建时执行这个函数并将 resolvereject 函数传入执行器让其调用从而改变 Promise 实例的状态
  • Promise 有三种状态分别为 pending(等待态)fulfilled(成功态)rejected(失败态)pending(等待态) 为初始态,并可以转化为 fulfilled(成功态)rejected(失败态)
  • 成功时,不可转为其他状态,且必须有一个不可改变的值(value
  • 失败时,不可转为其他状态,且必须有一个不可改变的原因(reason
  • fulfilledCbsrejectedCbs 意义在于可以支持一个 promise 实例状态改变后, then 函数中可以有若干个该状态的回调函数,依次执行
  • 如果用户给构造函数传的是一个同步函数,里面的 resolvereject 会立即执行,比 then 还执行的早,那 then 里面注册的回调就没机会运行了,所以要给他们加个 queueMicrotask

then

function resolvePromise(promise2, x, reslove, reject) {
	if (promise2 === x) {
		return reject(
			new TypeError(
				'TypeError: Chaining cycle detected for promise #<Promise>'
			)
		)
	}
	let called = false
	if (x instanceof Object) {
		try {
			let then = x.then
			if (typeof then === 'function') {
				then.call(
					x,
					(y) => {
						if (called) return
						called = true
						resolvePromise(promise2, y, reslove, reject)
					},
					(r) => {
						if (called) return
						called = true
						reject(r)
					}
				)
			} else {
				reslove(x)
			}
		} catch (error) {
			if (called) return
			called = true
			reject(error)
		}
	} else {
		reslove(x)
	}
}

class JetPromise {
then(onFulfilled, onRejected) {
		onFulfilled =
			typeof onFulfilled === 'function' ? onFulfilled : (value) => value
		onRejected =
			typeof onRejected === 'function'
				? onRejected
				: (err) => {
						throw err
				  }
		let promise2 = new JetPromise((resolve, reject) => {
			if (this.status === FULFILLED) {
				queueMicrotask(() => {
					try {
						let x = onFulfilled(this.value)
						resolvePromise(promise2, x, resolve, reject)
					} catch (error) {
						reject(error)
					}
				})
			}
			if (this.status === REJECTED) {
				queueMicrotask(() => {
					try {
						let x = onRejected(this.reason)
						resolvePromise(promise2, x, resolve, reject)
					} catch (error) {
						reject(error)
					}
				})
			}
			if (this.status === PENDING) {
				this.fulfilledCbs.push(() => {
					try {
						let x = onFulfilled(this.value)
						resolvePromise(promise2, x, resolve, reject)
					} catch (error) {
						reject(error)
					}
				})
				this.rejectedCbs.push(() => {
					try {
						let x = onRejected(this.reason)
						resolvePromise(promise2, x, resolve, reject)
					} catch (error) {
						reject(error)
					}
				})
			}
		})
		return promise2
	}

	catch(errorCb) {
		return this.then(null, errorCb)
	}

	finally(fn) {
		return this.then(fn, fn)
	}
}
  • 当状态 statefulfilled,则执行 onFulfilled,传入 this.value。当状态 staterejected,则执行 onRejected,传入 this.reasonvaluereason 依次作为他们的第一个参数
  • 如果传入的 onFulfilled 非函数类型或者是没有传该参数,那么就给予一个默认向下传递 value 的函数,将 value 交给下一个 then 去处理。如果 onRejected 非函数类型或者是没有传该参数,那我们将 reason 直接抛出,交给下一个 then 去处理
  • 解决异步实现。当 resolve 在异步任务内执行,thenstate 还是 pending 等待状态 我们就需要在 then 调用的时候,将成功和失败存到各自的数组,一旦 reject 或者 resolve,就调用它们(又是发布-订阅模式)
  • 解决链式调用。我们常常用到 new Promise().then().then(),可以在 then 函数后再次向下 then,为了达成链式我们可以在再 then 中返回一个 promise 实例
  • onFulfilledonRejected 中也有可能返回一个 promise 对象,所以我们需要保存 onFulfilledonRejected 的返回值暂存为 x,传入 resolvePromise 进行判断
  • resolvePromise 首先判断 onFulfilledonRejected 返回的值是不是 then 中返回的 promise 实例,如果是,那么就是自己等待自己完成,造成了循环引用,对标原版 promise 报错 'TypeError: Chaining cycle detected for promise #<Promise>'
  • 尝试判断 x 是否为 promise 对象,如果不是,代表 x 直接 resolve(x) ,如果是,就递归获得该 promise 实例的结果(因为可能会多重嵌套 promise),并始终保证调用的是将 then 函数的 resolvereject,使用 called 保证 resolvereject 只会调用一个
  • promisesA+ 规范规定 onFulfilledonRejected 不能同步被调用,必须异步调用。而且我们在 then 方法中是否循环等待时,也需要返回的 promsie 先创建才能判断。所以我们就用 queueMicrotask 添加一个微任务,解决异步问题
  • catchfinally 本质上还是 thencatch 是一个只有处理错误回调的 api,所以我们直接 return this.then(null, errorCb)finally 是无论 promise 成功还是失败总会执行的回调,一般在最后使用,关闭一些流事件,直接 return this.then(fn, fn)

使用 promises-aplus-tests 测试手写promise过程

JetPromise.defer = JetPromise.deferred = function () {
	let dfd = {}
	dfd.promise = new JetPromise((resolve, reject) => {
		dfd.resolve = resolve
		dfd.reject = reject
	})
	return dfd
}

module.exports = JetPromise

872个测试用例全部通过
在这里插入图片描述

其他Promise’s Api

Promise.resolve

  • 模仿 Promise 实现了两个静态方法,兼容非 Promise 数据,处理为 Promise 对象
static resolve = (param) => {
	if (param instanceof JetPromise) {
		return param
	}
	return new JetPromise((resolve) => {
		resolve(param)
	})
}

static reject = (param) => {
	return new JetPromise((resolve, reject) => {
		reject(param)
	})
}

Promise.all

  • 所有的 promiseresolve时,返回的 promiseresolve 一个数组,结果是顺序的所有 promise resolve 的结果
  • 如果任意一个 promisereject,由 Promise.all 返回的 promise 就会立即 reject,并且带有的就是这个 error
static all(PromiseArr) {
	return new JetPromise((resolve, reject) => {
		if (!PromiseArr instanceof Array) {
			reject(new TypeError('param`s type must be Array'))
		}
		let cnt = 0
		let res = []
		for (let i = 0; i < PromiseArr.length; i++) {
			JetPromise.resolve(PromiseArr[i])
				.then(
					(value) => {
						res[i] = value
						cnt++
						if (cnt === PromiseArr.length) {
							return resolve(res)
						}
					},
					(reason) => {
						return reject(reason)
					}
				)
				.catch((e) => {
					return reject(e)
				})
		}
	})
}

Promise.allSettled

  • Promise.allSettled 等待所有的 promise 都被 settle,无论结果如何。结果数组具有:
  • {status:“fulfilled”, value:result} 对于成功的响应
  • {status:“rejected”, reason:error} 对于 error
static allSettled(PromiseArr) {
	return new JetPromise((resolve, reject) => {
		if (!PromiseArr instanceof Array) {
			reject(new TypeError('param`s type must be Array'))
		}
		let cnt = 0
		let res = []
		for (let i = 0; i < PromiseArr.length; i++) {
			JetPromise.resolve(PromiseArr[i]).then(
				(value) => {
					res[i] = {
						status: 'fulfilled',
						value: value,
					}
					cnt++
					if (cnt === PromiseArr.length) {
						return resolve(res)
					}
				},
				(reason) => {
					res[i] = {
						status: 'rejected',
						reason: reason,
					}
					cnt++
					if (cnt === PromiseArr.length) {
						return resolve(res)
					}
				}
			)
		}
	})
}

Promise.race

  • 只等待第一个 settled 的 promise 并获取其结果(或 error)
static race(PromiseArr) {
	return new JetPromise((resolve, reject) => {
		if (!PromiseArr.length) return resolve()
		PromiseArr.forEach((promise) => {
			JetPromise.resolve(promise)
				.then(
					(value) => {
						return resolve(value)
					},
					(reason) => {
						return reject(reason)
					}
				)
				.catch((e) => {
					return reject(e)
				})
		})
	})
}

Promise.any

  • 只等待第一个 fulfilled 的 promise,并将这个 fulfilled 的 promise 返回
  • 如果给出的 promise 都 rejected,那么则返回 rejected 的 promise 和 AggregateError 错误类型的 error 实例——一个特殊的 error 对象,在其 errors 属性中存储着所有 promise error
static any(PromiseArr) {
	return new JetPromise((resolve, reject) => {
		if (!PromiseArr.length)
			return reject(new AggregateError('All promises were rejected'))
		let errs = []
		let cnt = 0
		for (let i = 0; i < PromiseArr.length; i++) {
			JetPromise.resolve(PromiseArr[i]).then(
				(value) => {
					return resolve(value)
				},
				(reason) => {
					errs[i] = reason
					cnt++
					if (cnt === PromiseArr.length) {
						return reject(new AggregateError(errs))
					}
				}
			)
		}
	})
}
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 16:08:57  更:2022-04-06 16:09:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:23:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码