Promise学习-手写一个promise

学习了Promise的A+规范,以及手写一个Promise后,我对Promise学到的结果。

平常也有用到过promise来处理异步,先回顾下promise的用法

new Promise((resolve,reject) => {异步操作并改变状态}).then(onfullfilled, onrejected)

const myPromise = new Promise((resolve,reject) => {
	//写一些异步操作,比如ajax请求
	if(window.onError){
		reject('页面加载失败');
	}
	setTimeout(() => {
		resolve('异步返回结果'); 
	}, 1000)
}).then((data) => {
	console.log(data);    // 异步返回结果
}).catch((reason)=>{
	console.log(reason);  // 页面加载失败
})

这个promise其实就是处理一件一段时间之后才能得到结果的事件,promise将将要执行的事件作为函数传入,并在得到结果时,resolve或者reject改变promise的状态并且改变结果,然后通过then或catch执行得到结果之后的事情。

一、每个promise都有三个状态

pending 初始化,可改变
fullfilled 成功,不可改变
rejected 失败,不可改变

promise的状态改变流程
Promise学习-手写一个promise_第1张图片

二、resolve和reject函数

resolve:实参是定义在构造函数中的函数,
作用:
改变状态pending => fullfilled
改变结果 value = data
reject:实参是定义在构造函数中的函数,
作用:
改变状态pending => rejected,
改变结果 reason= reason

三、then:作为promise提供的一个方法, 用来访问最终的结果, 无论是value还是reason.

promise.then(onFulfilled, onRejected)

1.onFulfilled和onRejected,必须传入函数,如果不是函数,则改变为直接返回value或者reason的函数。
2.onFulfilled和onRejected只有当状态改变为相应的值后才可以执行,并将value和reason作为参数,且只可以执行一次
3.onFulfilled 和 onRejected 应该是微任务,这里用queueMicrotask来实现微任务的调用
4.then方法可以被调用多次
4.1promise状态变成 fulfilled 后,所有的 onFulfilled 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onFulfilled的回调)
4.2promise状态变成 rejected 后,所有的 onRejected 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onRejected的回调)
5.then 应该返回一个promise

 promise2 = promise1.then(onFulfilled, onRejected);
5.1 onFulfilled 或 onRejected 执行的结果为x, 调用 resolvePromise
5.2 如果 onFulfilled 或者 onRejected 执行时抛出异常e, promise2需要被reject
5.3 如果 onFulfilled 不是一个函数, promise2 以promise1的value 触发fulfilled
5.4 如果 onRejected 不是一个函数, promise2 以promise1的reason 触发rejected

不管是new Promise((resolve,reject)=> {})还是then((data) => {}),传入的函数都是在构造函数内执行的,所以实参是在构造函数内定义赋值的,所以我们再手写一个promise时,需要定义resolve和reject函数,以及得到的value,作为函数的实参

四、resolvePromise 就是处理then中onFulfilled 或 onRejected 返回的数据,并最终resolve或者reject掉promise2,改变promise2的状态,只有改变了状态,才能继续.then链式操作。

代码主要实现了A+规范里的内容:
如果 promise2 和 x 相等,那么 reject TypeError
如果 x 是一个 promsie
如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected.
如果 x 被 fulfilled, fulfill promise with the same value.
如果 x 被 rejected, reject promise with the same reason.
如果 x 是一个 object 或者 是一个 function
let then = x.then.
如果 x.then 这步出错,那么 reject promise with e as the reason.
如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
rejectPromise 的 入参是 r, reject promise with r.
如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
如果调用then抛出异常e
如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略
则,reject promise with e as the reason

五、手写一个promise

new MyPromise((resolve,reject) => {}).then(() => {}, () => {})
new MyPromise((resolve,reject) => {}).then(() => {}).catch(() => {})

从用法可以看出我们需要做的功能有
1.实现一个构造函数
2.构造函数接收一个函数作为参数
3.我们要再构造函数里面执行传进来的参数,并且传递实参resolve,reject, 作为提供给用户更改promise状态的方法
4.实现resolve,reject
5.实现then:
接收两个函数参数,分别在状态改变成对应值时触发
为了实现链式操作,继续让then返回一个promise
6.从promiseA+规范得知,我们还要实现一个resolvePromise去处理then返回的数据

1. promise是通过实例化的形式使用的,所以我们使用class写一个构造函数的方式实现。

class MyPromise {
	construcor() {}
}

2.promise始终是围绕三个状态状态实现异步的,所以我们定义下三个状态,并且给实例的promise状态、resolve结果值和reject原因赋初始值值。

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
	construcor() {
        this.status = PENDING; // 初始状态为pending
        this.value = null;
        this.reason = null;
	}
}

3.我们调用的resolve和reject方法去改变promise状态并且将他的参数赋值。

class MPromise {
    constructor() {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
    }

    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }

    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
}

4.这个Promise类应该接收一个函数作为promise的入参,默认一进来就执行这个函数,并且把resolve和reject作为这个函数的入参抛出去,让使用的promise的人可以控制promise状态的改变。

class MPromise {
    constructor(fn) {
        // 初始状态为pending
        this.status = PENDING;
        this.value = null;
        this.reason = null;
        // 对用户自己传进来执行的代码块一般都做下try-catch
        try {
            // 这里绑定this是因为resolve执行的时候this的指向不一定是这个promise了,而我们要让他指向这里才能使用this.status...
        	fn(this.resolve.bind(this),this.reject.bind(this))
        } catch(e) {
			this.reject(e)
		}
    }
    
    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }

    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
}

5.接下来实现一下请求状态改变后的方法,then()

a.接收两个函数参数,分别在状态改变成对应值时触发
为了实现链式操作,继续让then返回一个promise

 then(onFulfilled, onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled)
      ? onFulfilled
      : (value) => {
          return value;
        };
    const realOnRejected = this.isFunction(onRejected)
      ? onRejected
      : (value) => {
          return value;
        };
    const promise2 = new MPromise((resolve, reject) => {
      switch (this.status) {
        case FULFILLED: {
          realOnFulfilled(value);
        }
        case REJECTED: {
          realOnRejected(value);
        }
      }
    });
    return promise2;
  }
  isFunction(value) {
    return typeof value === "function";
  }

b.考虑到如果传入的函数中执行异步之后resolve,而执行then中的函数时,还状态还是pending

 // 定义两个数组存放还没有执行的回调函数
 FULLFILLED_CALLBACK_LIST = [];
  REJECTED_CALLBACK_LIST = [];

 // 做一下status的监听
  _status = PENDING;
  get status() {
    return _status;
  }
  set status(value) {
    this._status = value;
    if (value === FULFILLED) {
      this.FULLFILLED_CALLBACK_LIST.forEach((callback) => callback(this.value));
    } else if (value === REJECTED) {
      this.REJECTED_CALLBACK_LIST.forEach((callback) => callback(this.reason));
    }
  }
then(onFulfilled, onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled)
      ? onFulfilled
      : (value) => {
          return value;
        };
    const realOnRejected = this.isFunction(onRejected)
      ? onRejected
      : (value) => {
          return value;
        };
    const promise2 = new MPromise((resolve, reject) => {
      switch (this.status) {
         case FULFILLED: {
          try {
            realOnFulfilled(value);
          } catch (error) {
            reject(error);
          }
        }
        case REJECTED: {
          try {
            realOnRejected(value);
          } catch (error) {
            reject(error);
          }
        }
        case PENDING: {
          this.FULLFILLED_CALLBACK_LIST.push(realOnFulfilled);
          this.REJECTED_CALLBACK_LIST.push(realOnRejected);
        }
      }
    });
    return promise2;
  }
  isFunction(value) {
    return typeof value === "function";
  }

6. 按照规范实现下resolePromise

 resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
      reject(new TypeError("the promise and the return value are the same"));
    }

    if (x instanceof MPromise) {
      x.then((y) => {
        resolvePromise(promise2, y, resolve, reject);
      });
    } else if (typeof x === "object" || this.isFunction(x)) {
      if ((x = null)) {
        return resolve(x);
      }

      let then = null;

      try {
        then = x.then;
      } catch (error) {
        return reject(error);
      }

      if (this.isFunction(then)) {
        const called = false;
        try {
          then(
            x,
            (y) => {
              if(called) return;
              called = true;
              resolvePromise(promise2, y, resolve, reject);
            },
            (r) => {
              if(called) return;
              called = true;
              reject(r);
            }
          );
        } catch (error) {
          if (called) return;
          reject(error)
        }
      } else {
        resolve(x);
      }
    } else {
      resolve(x);
    }
  }

7.所有的onFullFilled和onRejected

使用queueMicrotask

8.实现catch

catch其实就是调用then方法,传入一个回调函数

catch(onRejected){
	return this.then(null, onRejected);
}

9.实现静态方法resolve、reject、finally

这里注意resolve传入的参数有可能是promise也有可能是一个值

static resolve(value) {
	if(value instanceof MyPromise) {
		return value;
	}
	return new MyPromise((resolve)=> resolve(value));
	
}
static reject(reason) {
	return new MyPromise((resolve, reject) => reject(reason));
}
// finally原则上也是then方法实现,注意传入的回调中不用传参,再次点then才能获取到value或者err
static finally(callback) {
	return this.then(
	(value) => {
		return MyPromise.resolve(callback()).then(() => value); // 无论状态变为fullfilled还是rejected都执行回调
	}, 
	(err) => {
		return MyPromise.resolve(callback()).then(() => {throw err;}); // 无论状态变为fullfilled还是rejected都执行回调
	})
}

9.实现all、race

注意all和race都是异步过程中返回的;

race(arr) {
	const len = arr.length;
	return new MyPromise((resolve,reject) => {
		if(len === 0) {
			resolve();
		}
		for(let i = 0; i < len; i++){
			MyPromise.resolve(arr[i]).then(
				(value) => {
					resolve(value);
				},
				(reason) => {
					reject(reason)	
				}
			)
		}
		
	})
}

all(arr) {
	const len = arr.length;
	return new MyPromise((resolve,reject) => {
		let res = [];
		let count = 0;
		if(len === 0) {
			resolve();
		}
		for(let i = 0; i < len; i++){
			MyPromise.resolve(arr[i]).then(
				(value) => {
					res[i] = value;
					count++;
					if(count === len) {
						resolve(res);
					}
				},
				(reason) => {
					reject(reason)	
				}
			)
		}
		
	})
}

六、看几个面试题

1、打印输出

const test1 = new MyPromise((resolve) => {
  setTimeout(() => {
    resolve(123);
  }, 2000);
}).then(console.log);

console.log(test1); //pending value: null
setTimeout(() => {
  console.log(test1); // fullfilled value: undefined  then函数没有返回值则返回值为undefined即x=undefined,则promise在解析时则resolve(x)
}, 3000);

2、then返回的是一个新Promise, 那么原来promise实现的时候, 用数组来存回调函数有什么意义?

const test = new MPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(111);
  }, 1000);
})
  .then((value) => {})
  .then(() => {})
  .then(() => {})
  .then(() => {})
  .then(() => {});

/* 这个问题提出的时候, 应该是有一个假定条件, 就是链式调用的时候.

这个时候, 每一个.then返回的都是一个新promise, 所以每次回调数组FULFILLED_CALLBACK_LIST都是空数组.

针对这种情况, 确实用数组来存储回调没意义, 完全可以就用一个变量来存储。 */

// 但是还有一种promise使用的方式, 这种情况下, promise实例是同一个, 数组的存在就有了意义

const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
    }, 1000);
})

test.then(() => {});
test.then(() => {});
test.then(() => {});
test.then(() => {});

3、只有当onRjected执行报错的时候才会reject第二个promise,否则最终都会执行到resolve(x)

const test = new MPromise((resolve, reject) => {
  setTimeout(() => {
      reject(111);
  }, 1000);
}).catch((reason) => {
  console.log('报错' + reason);
  console.log(test)  // pending  
});

setTimeout(() => {
  console.log(test); // fullfilled
}, 3000)

完整代码

/* 
new MyPromise((resolve,reject) => {}).then(() => {}, () => {})
new MyPromise((resolve,reject) => {}).then(() => {}).catch(() => {})

*/

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
  FULLFILLED_CALLBACK_LIST = [];
  REJECTED_CALLBACK_LIST = [];
  _status = PENDING;
  constructor(fn) {
    this.status = PENDING;
    this.value = null;
    this.reason = null;
    try {
      fn(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error);
    }
  }

  get status() {
    return this._status;
  }
  set status(value) {
    this._status = value;
    if (value === FULFILLED) {
      this.FULLFILLED_CALLBACK_LIST.forEach((callback) => callback(this.value));
    } else if (value === REJECTED) {
      this.REJECTED_CALLBACK_LIST.forEach((callback) => callback(this.reason));
    }
  }

  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) {
    const realOnFulfilled = this.isFunction(onFulfilled)
      ? onFulfilled
      : (value) => {
          return value;
        };
    const realOnRejected = this.isFunction(onRejected)
      ? onRejected
      : (reason) => {
          throw reason;
        };
    const promise2 = new MyPromise((resolve, reject) => {
      const fulfilledMicrotask = () => {
        queueMicrotask(() => {
          try {
            const x = realOnFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };
      const rejectedMicrotask = () => {
        queueMicrotask(() => {
          try {
            const x = realOnRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };
      switch (this.status) {
        case FULFILLED: {
          fulfilledMicrotask();
          break;
        }
        case REJECTED: {
          rejectedMicrotask();
          break;
        }
        case PENDING: {
          this.FULLFILLED_CALLBACK_LIST.push(fulfilledMicrotask);
          this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask);
        }
      }
    });
    return promise2;
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
      return reject(
        new TypeError("the promise and the return value are the same")
      );
    }

    if (x instanceof MyPromise) {
      queueMicrotask(() => {
        x.then((y) => {
          resolvePromise(promise2, y, resolve, reject);
        }, reject);
      });
    } else if (typeof x === "object" || this.isFunction(x)) {
      if ((x = null)) {
        return resolve(x);
      }

      let then = null;

      try {
        then = x.then;
      } catch (error) {
        return reject(error);
      }

      if (this.isFunction(then)) {
        const called = false;
        try {
          then.call(
            x,
            (y) => {
              if (called) return;
              called = true;
              resolvePromise(promise2, y, resolve, reject);
            },
            (r) => {
              if (called) return;
              called = true;
              reject(r);
            }
          );
        } catch (error) {
          if (called) return;
          reject(error);
        }
      } else {
        resolve(x);
      }
    } else {
      resolve(x);
    }
  }

  isFunction(value) {
    return typeof value === "function";
  }
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    }
    return new MyPromise((resolve) => {
      resolve(value);
    });
  }
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }
  static race(promiseList) {
    const length = promiseList.length;
    return new MyPromise((resolve, reject) => {
      if (promiseList.length === 0) return resolve();
      promiseList.forEach((promise) =>
        MyPromise.resolve(promise).then(
          (value) => resolve(value),
          (reason) => reject(reason)
        )
      );
    });
  }
 static all(promiseList) {
    return new MyPromise((resolve, reject) => {
      let res = [];
      let count = 0;
      if (promiseList.length === 0) return resolve();
      promiseList.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          (value) => {
            res[index] = value;
            count++;
            if (count === promiseList.length) {
              resolve(res);
            }
          },
          (reason) => {
            reject(reason);
          }
        );
      });
    });
  }
  finally(callback) {
    return this.then(
      (value) => {
        return MyPromise.resolve(callback()).then(() => value);
      },
      (err) => {
        return MyPromise.resolve(callback()).then(() => {
          throw err;
        });
      }
    );
  }
}

// all的测试
// let p1 = MyPromise.resolve(123);
// let p2 = MyPromise.resolve(456);
// let p3 = MyPromise.resolve(789);

// const test = MyPromise.all([p1, p2, p3])
//   .then((result) => {
//     console.log(result, "then");
//   })
//   .catch((reason) => {
//     console.log(reason, "catch");
//   });

// 课后练习1
const test1 = new MyPromise((resolve) => {
  setTimeout(() => {
    resolve(123);
  }, 2000);
}).then(console.log);

console.log(test1); //pending value: null
setTimeout(() => {
  console.log(test1); // fullfilled value: undefined  then函数没有返回值则返回值为undefined即x=undefined,则promise在解析时则resolve(x)
}, 3000);

// 课后练习2: .then返回的是一个新Promise, 那么原来promise实现的时候, 用数组来存回调函数有什么意义?
const test = new MPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(111);
  }, 1000);
})
  .then((value) => {})
  .then(() => {})
  .then(() => {})
  .then(() => {})
  .then(() => {});

/* 这个问题提出的时候, 应该是有一个假定条件, 就是链式调用的时候. 

这个时候, 每一个.then返回的都是一个新promise, 所以每次回调数组FULFILLED_CALLBACK_LIST都是空数组. 

针对这种情况, 确实用数组来存储回调没意义, 完全可以就用一个变量来存储。 */

// 但是还有一种promise使用的方式, 这种情况下, promise实例是同一个, 数组的存在就有了意义

const test = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
    }, 1000);
})

test.then(() => {});
test.then(() => {});
test.then(() => {});
test.then(() => {});

// 课后练习3: 只有当onRjected执行报错的时候才会reject第二个promise,否则最终都会执行到resolve(x)
const test = new MPromise((resolve, reject) => {
  setTimeout(() => {
      reject(111);
  }, 1000);
}).catch((reason) => {
  console.log('报错' + reason);
  console.log(test)  // pending  
});

setTimeout(() => {
  console.log(test); // fullfilled
}, 3000)

你可能感兴趣的:(学习笔记,node.js,javascript)