想要源码的可以看这里,里面也有一些其他的知识
想要手写一个promise,首先就要了解promise,想必大家都被过一些promise的面试题,知道一些promise的用法,主要考的就是一种异步编程的思想。
我们先来看看直接输出一个promise对象会是什么,通过代码:
var p = new Promise((resolve,reject)=>{}); console.log(p);
可以看到输出结果Promise大致由它的状态PromiseState,它的值PromiseResult和它原型上面的方法组成。
promise对象有一个函数当作参数,函数里又分别有两个参数,分别是resolve和reject,当调用resolve()时就会运行prototype上的then()方法,当调用reject()时就会运行prototype上的catch()方法,这里的then,catch都是微任务,所谓微任务就是之宏任务运行完之后所运行的任务。就好比去银行办业务,你的要办的业务相当于宏任务,等你办完后银行工作人员会推荐与你业务相关的拓展业务,这个拓展业务就相当于微任务。
promise的状态有三种:等待(pending)、已完成(fulfilled)、已拒绝(rejected),并且状态只能由等待到完成或者等待到拒接。
了解promise之后我们就能把基本的架构写出来了。
function MyPromise(fn) { // promise 的状态 this.PromiseState = "pendding"; // promise 的值 this.PromiseResult = undefined; var resolve = (value) => {}; var reject = (errValue) => {}; if (fn) { fn(resolve, reject); } }
这里定义初始状态pedding,和初始值undefined,resolve,reject两个方法。
首先先完成then的成功运作,如何能让如下代码成功输出:
var p = new MyPromise((resolve, reject) => { resolve("resolve"); }); p.then((res) => { console.log(res); console.log("then执行"); });
这里就需要完善resolve和原型对象上写then方法:
// 定义一个函数对象,用来注册then中的callback this.thenCallback = undefined; var resolve = (value) => { // 更改promise的状态和值 if (this.PromiseState == "pendding") { this.PromiseState = "fulfilled"; this.PromiseResult = value; if (value instanceof MyPromise) { value.then((res) => { if (this.thenCallback) { this.thenCallback(res); } }); } else { setTimeout(() => { if (this.thenCallback) { this.thenCallback(value); } }); } } };
MyPromise.prototype.then = function (callback) { return new MyPromise((resolve, reject) => { this.thenCallback = (value) => { // 在使用链式调用的时候,可能第一个调用的不是catch // 使用我们在做检测时会借助then来将catch的信息向下传递 // 所以我们检测到触发thenCallback的对象是rejected时 // 我们就继续调用下一个reject if (this.promiseState == "rejected") { reject(value); } else { var res = callback(value); // 这里防止中间返回是一个promise对象它会继续找then,直接让他调用reject if (res instanceof MyPromise && res.promiseState == "rejected") { res.catch((errValue) => { reject(errValue); }); } else { // 这里定义给变量res在调用resolve是为解决.then()的链式调用 resolve(res); } } }; }); };
定义一个函数对象,用来注册then中的callback,首先判断promise的状态,如果是pendding则转换成fulfilled,并将参数值赋值给promiseResult;
if (value instanceof MyPromise)是为了判断value是否是一个Promise对象,如果是就使用自己定义的then,因为then时异步执行的,所以使用setTimeout
当then链式调用时,如then().then(),第一个then中如果有返回值,那么这个返回值将会作为第二个then中的参数,所以需要每个then返回一个新的promise对象return new MyPromise()。
这里再封装一个MyPromise.resolve的快捷调用:
MyPromise.resolve = (value) => { return new MyPromise((resolve, reject) => { resolve(value); }); };
catch与then大致相同,只需要稍作修改。这里完善reject方法和原型上的catch:
this.catchCallback = undefined; var reject = (errValue) => { if (this.promiseState == "pendding") { this.promiseState = "rejected"; this.promiseResult = errValue; // 判断写没写catch() setTimeout(() => { if (this.catchCallback) { this.catchCallback(errValue); } else if (this.thenCallback) { this.thenCallback(errValue); } else { throw "catch is not defined!!!!"; } }); } };
首先判断promise的状态,如果是pendding则转换成frejected,并将参数值赋值给promiseResult;
这里有可能再调用catch前还调用了then方法,所以使用else if判断是不是,时就运行this.thenCallback(errValue);,因为之前在then会判断promise状态是不是rejected,如果是就重新调用reject()方法。
MyPromise.prototype.catch = function (callback) { return new MyPromise((resolve, reject) => { this.catchCallback = (errValue) => { var res = callback(errValue); reject(errValue); }; }); };
这里也继续封装一个MyPromise.reject的快捷调用:
MyPromise.reject = (errValue) => { return new MyPromise((resolve, reject) => { reject(errValue); }); };
all和race传入的参数都是一个数组,all将会等最长时间结束后按数组的顺序,而race则会执行最早的那一个。
MyPromise.all = (promiseArr) => { let resArr = []; return new MyPromise((resolve, reject) => { promiseArr.forEach((item, index) => { item .then((res) => { resArr[index] = res; var allResolve = promiseArr.every((_item) => { return _item.promiseState == "fulfilled"; }); // 判断传过来的数组中所有promise对象状态都已完成 if (allResolve) { resolve(resArr); } }) .catch((err) => { reject(err); }); }); }); };
MyPromise.race = (promiseArr) => { let resArr = []; return new MyPromise((resolve, reject) => { promiseArr.forEach((item, index) => { item .then((res) => { resolve(res); }) .catch((err) => { reject(err); }); }); }); };
手写promise的化没有长时间的叙述的话不好将清楚,建议找到将promise源码的视频一步一步分析