手写promise源码
promise状态pending
首先我们需要了解,promise有三个状态
- promise状态只能改变一次,只有一个结果数据
- pending----->fulfilled::成功数据value
- pending----->rejected::失败原因reason
通过我们平时使用promise对象,我们可以了解到,Promise构造函数返回一个Promise对象实例,这个返回的Promise对象具有一个then 方法。在 then 方法中,调用者可以定义两个参数,分别是onfulfilled和onrejected,它们都是函数类型的参数。其中,onfulfilled通过参数可以获取Promise对象经过resolve处理后的值,onrejected可以获取Promise对象经过reject处理后的值。通过这个值,我们来处理异步操作完成后的逻辑。
对此,我们可以写出来以下代码结构:
function Mypromise(executor) {
this.status = 'pending'
this.value = undefined
this.reason = undefined
let resolve = (value)=>{
this.value = value
}
let reject = (reason)=>{
this.reason = reason
}
executor(resolve,reject)
}
Mypromise.prototype.then=function(onFulfilled,onRejected){
onFulfilled(this.value)
onRejected(this.reason)
}
因为promise的状态是不可逆的,所以我们需要判断每次执行resolve的状态:
function Mypromise(executor) {
this.status = 'pending'
this.value = undefined
this.reason = undefined
let resolve = (value) => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
}
}
executor(resolve, reject)
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled = Function.prototype, onRejected = Function.prototype) {
if (this.status === 'fulfilled') {
onFulfilled(this.value)
}
if (this.status === 'rejected') {
onRejected(this.reason)
}
}
到这里我们就完成了简单的promise的实现。但是我们的代码实现不了异步的回调功能:
const p1 = new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('success')
},2000)
})
p1.then(y=>{
console.log(y);
})
promise异步实现完善
上边无法实现异步回调的原因是,正常来讲,上述代码会在 2s 后输出 success,但是现在,代码并没有输出任何信息。这是为什么呢? 原因很简单,因为我们的实现逻辑全是同步的。上述代码在实例化一个Promise的构造函数时,会在setTimeout逻辑中调用resolve,也就是说,2s后才会调用resolve方法,更改Promise实例状态。而结合我们的实现,then方法中的onfulfilled是同步执行的,它在执行时thisstatus仍然为pending,并没有做到“2s后再执行onfulfilled”。 那该怎么办呢?我们似乎应该在合适的时间去调用onfulfilled方法,这个合适的时间应该是开发者调用resolve 的时刻,那么我们先在状态(status)为pending时把开发者传进来的onfulfilled方法存起来,再在 resolve 方法中执行即可。
修改代码如下:
function Mypromise(executor) {
this.status = 'pending'
this.value = undefined
this.reason = undefined
this.onFulfilledFunc;
this.onRejectedFunc;
let resolve = (value) => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFulfilledFunc(this.value)
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedFunc(this.reason)
}
}
executor(resolve, reject)
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled = Function.prototype, onRejected = Function.prototype) {
if (this.status === 'fulfilled') {
onFulfilled(this.value)
}
if (this.status === 'rejected') {
onRejected(this.reason)
}
if(this.status === 'pending'){
this.onFulfilledFunc = onFulfilled
this.onRejectedFunc = onRejected
}
}
}
但是伴随这一个新的问题出现了,下边这种情况下应该输出2个success但是最后却只输出了最后一个success,因为我们then方法调用后会把上一个的onFulfilled或onRejected覆盖掉,对于此情况我们需要把他们都放到一个数组中,当我们resolve的时候循环遍历执行即可:
function Mypromise(executor) {
this.status = 'pending'
this.value = undefined
this.reason = undefined
this.onFulfilledFunc = []
this.onRejectedFunc = []
let resolve = (value) => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFulfilledFunc.forEach(func=>func())
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedFunc.forEach(func=>func())
}
}
executor(resolve, reject)
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled = Function.prototype, onRejected = Function.prototype) {
if (this.status === 'fulfilled') {
onFulfilled(this.value)
}
if (this.status === 'rejected') {
onRejected(this.reason)
}
if(this.status === 'pending'){
this.onFulfilledFunc.push(()=>{
onFulfilled(this.value)
})
this.onRejectedFunc.push(()=>{
onRejected(this.reason)
})
}
}
当我们执行下面代码时,应先打印出 111 ,在打印success,但是我们的promise先打印除了success才出现111,所以我们需要把then的执行改为微任务队列,可以使用借助setimeout也可以queueMicrotask来执行微任务
const p1 = new MyPromise((resolve,reject)=>{
resolve('success')
})
p1.then(y=>{
console.log(y);
})
p1.then(y=>{
console.log(y);
})
console.log(111);
修改完成后的代码如下:
function Mypromise(executor) {
this.status = 'pending'
this.value = undefined
this.reason = undefined
this.onFulfilledFunc = []
this.onRejectedFunc = []
let resolve = (value) => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFulfilledFunc.forEach(func => func())
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedFunc.forEach(func => func())
}
}
executor(resolve, reject)
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled = Function.prototype, onRejected = Function.prototype) {
if (this.status === 'fulfilled') {
setTimeout(() => {
onFulfilled(this.value)
})
}
if (this.status === 'rejected') {
setTimeout(() => {
onRejected(this.reason)
})
}
if (this.status === 'pending') {
this.onFulfilledFunc.push(() => {
setTimeout(() => {
onFulfilled(this.value)
})
})
this.onRejectedFunc.push(() => {
setTimeout(() => {
onRejected(this.reason)
})
})
}
}
到此我们已经简单的实现了promise的同步和异步使用了。
promise的链式调用
promise的链式调用上一个的then方法需要返回一个promise对象,我们才可以调用,我们在then方法里边执行的onRejected和onFulfilled的返回值就是下一个then方法所需要的resolve的值,所以我们可以这样做:
function Mypromise(executor) {
this.status = 'pending'
this.value = undefined
this.reason = undefined
this.onFulfilledFunc = []
this.onRejectedFunc = []
let resolve = (value) => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFulfilledFunc.forEach(func => func())
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedFunc.forEach(func => func())
}
}
executor(resolve, reject)
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled = Function.prototype, onRejected = Function.prototype) {
const promise2 = new Mypromise((resolve,reject)=>{
let x;
if (this.status === 'fulfilled') {
setTimeout(() => {
x = onFulfilled(this.value)
resolve(x)
})
}
if (this.status === 'rejected') {
setTimeout(() => {
x = onRejected(this.reason)
reject(x)
})
}
if (this.status === 'pending') {
this.onFulfilledFunc.push(() => {
setTimeout(() => {
x = onFulfilled(this.value)
resolve(x)
})
})
this.onRejectedFunc.push(() => {
setTimeout(() => {
x = onRejected(this.reason)
reject(x)
})
})
}
})
return promise2
}
但是promise里边也有肯能继续返回一个promise所以我们需要对x做判断,如下边这样:
const p1 = new MyPromise((resolve,reject)=>{
resolve(new new MyPromise((resolve,reject)=>{
resolve('success')
}))
})
p1.then(y=>{
console.log(y);
})
p1.then(y=>{
console.log(y);
})
所以我们需要写一个函数对x进行判断:
const resolvePromise = function(p2,x,resolve,reject){
// 如果promise和 result相等会陷入死循环
if(p2===x){
return reject(new TypeError('type error'))
}
let consumed = false
if((typeof x === 'function') || (typeof x === 'object' && x !== null)){
try{
let thenable = x.then
if(typeof thenable === 'function'){
thenable.call(x,(y)=>{
if(consumed) return
consumed = true
resolvePromise(p2,y,resolve,reject)
},err=>{
if(consumed) return
consumed = true
reject(err)
})
}else{
if(consumed) return
consumed = true
resolve(x)
}
}catch(err){
if(consumed) return
consumed = true
reject(err)
}
}else{
resolve(x)
}
}
}
完整的代码如下:
function Mypromise(executor) {
this.status = 'pending'
this.value = undefined
this.reason = undefined
this.onFulfilledFunc = []
this.onRejectedFunc = []
let resolve = (value) => {
if (this.status === 'pending') {
this.value = value
this.status = 'fulfilled'
this.onFulfilledFunc.forEach(func => func())
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason
this.status = 'rejected'
this.onRejectedFunc.forEach(func => func())
}
}
// 防止promise初始化时报错
try{
executor(resolve, reject)
}catch(e){
reject(e)
}
}
// 这里暂时添加函数的原型给2个回调函数默认值,防止不传入函数回调报错
Mypromise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : data => data
onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
const promise2 = new Mypromise((resolve,reject)=>{
let x;
if (this.status === 'fulfilled') {
setTimeout(() => {
try{
x = onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
}
if (this.status === 'rejected') {
setTimeout(() => {
try{
x = onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
}
if (this.status === 'pending') {
this.onFulfilledFunc.push(() => {
setTimeout(() => {
try{
x = onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
})
this.onRejectedFunc.push(() => {
setTimeout(() => {
try{
x = onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
})
}
})
return promise2
}
const resolvePromise = function(p2,x,resolve,reject){
// 如果promise和 result相等会陷入死循环
if(p2===x){
return reject(new TypeError('type error'))
}
let consumed = false
if((typeof x === 'function') || (typeof x === 'object' && x !== null)){
try{
let thenable = x.then
if(typeof thenable === 'function'){
thenable.call(x,(y)=>{
if(consumed) return
consumed = true
resolvePromise(p2,y,resolve,reject)
},err=>{
if(consumed) return
consumed = true
reject(err)
})
}else{
if(consumed) return
consumed = true
resolve(x)
}
}catch(err){
if(consumed) return
consumed = true
reject(err)
}
}else{
resolve(x)
}
}
测试用例:
npm i promises-aplus-tests -g 运行命令 promises-aplus-tests 文件名称
用例编写
Mypromise.defer = Mypromise.deferred = function(){
let dfd = {}
dfd.promise = new Mypromise((resolve,reject)=>{
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Mypromise