前端日记
今天是前端日记的第五天,清楚了promise的使用后,接下来我们开始分析promise的原理以及做简单的实现,然后与大家一起分享。一个前端小白的学习之路。
promise分析
先上代码,实例化一个promise
var promise = new Promise(function (resolve, reject) {
//做一些异步操作
setTimeout(function () {
var num = parseInt(Math.random() * 10); //生成1-10的随机数
if (num <= 5) {
resolve(num);
} else {
reject(`数字太大了,我拿到了${num}`);
}
}, 2000);
});
从代码上看,Promise这个类中传一个函数作为参数,把这个参数的函数称为callback,并且这个callback函数是马上执行的,也就是在new Promise这个类的时候自动执行下面这个callback函数
function (resolve, reject) {
//做一些异步操作
setTimeout(function () {
var num = parseInt(Math.random() * 10); //生成1-10的随机数
if (num <= 5) {
resolve(num);
} else {
reject(`数字太大了,我拿到了${num}`);
}
}, 2000);
}
再看这个函数又有2个函数参数,分别是Promise的resolve()和 reject()方法,作用是让Promise的状态定型,那么我们思考如何马上执行callback函数并把resolve和reject方法以参数的形式传给callback函数,结合promise的介绍,我们可以先初步实现一个promise类
class Mpromise {
constructor(callback) {
this.resolveVal = undefined;//存放成功的值
this.rejectVal = undefined;//存放失败的值
this.status = "pending"; //默认是等待状态
//初始化时马上执行callback函数并把resolve和reject方法以参数的形式传递
try {
callback(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
resolve(val) {
//如果状态不是等待中则不做任何操作
if (this.status === "pending") {
this.resolveVal = val;
this.status = "resolve";
}
}
reject(val) {
//如果状态不是等待中则不做任何操作
if (this.status === "pending") {
this.rejectVal = val;
this.status = "reject";
}
}
}
是否有人好奇,this.resolve.bind(this)为什么要使用bind(this),是因为在callback函数内部resolve和reject的this指向window这个而不是promise所以要改变下this的指向否则会报错
promise.then分析
接下来就是要实现then方法,老规矩先观察
promise.then(function(successDate){
console.log(successDate)
},function(errorData){
console.log(errorData)
})
可以看到,then方法中是有2个参数分别是成功后的回调和失败后的回调,那么其实也很好实现,直接判断状态,执行相应回调就OK,先写个同步版的
//同步版的then方法
then(successFn, failFn) {
let self = this;
if (self.status === "pending") {
return;//没有成功也没有失败就不做处理
} else if (self.status === "resolve") {
successFn(self.resolveVal);
} else {
failFn(self.rejectVal);
}
}
var a1 = new Mpromise(function (resolve, reject) {
resolve("成功数据1");
});
a1.then(function(success){
console.log(success) //输出成功数据1
})
以上代码不难理解,当new promise的时候马上执行resolve方法,此时promise的状态已经固定为resolve,然后执行then方法把成功的值返回给回调函数,但是同步的往往不是我们所需要的,我们需要的是then能返回2秒(2秒只是个代词,代表异步的时间)后的结果,直接上全代码
class Mpromise {
constructor(callback) {
this.resolveVal = undefined;
this.rejectVal = undefined;
this.status = "pending"; //默认是等待状态
this.resolveList = [];//成功回调数组
this.rejectList = [];//失败回调数组
// callback(self.success, self.fail);
//初始化时马上执行callback
try {
callback(this.success.bind(this), this.fail.bind(this));
} catch (error) {
this.fail(error);
}
}
success(val) {
if (this.status === "pending") {
this.resolveVal = val;
this.status = "resolve";
this.resolveList.length &&
this.resolveList.forEach((fn) => {
fn(this.resolveVal);
});
}
}
fail(val) {
if (this.status === "pending") {
this.rejectVal = val;
this.status = "reject";
this.rejectList.length &&
this.rejectList.forEach((fn) => {
fn(this.rejectVal);
});
}
}
isFunction(obj) {
return "function" === typeof obj;
}
then(successFn, failFn) {
let self = this;
if (self.status === "pending") {
self.isFunction(successFn) && self.resolveList.push(successFn);//添加到resolveList数组中
self.isFunction(failFn) && self.rejectList.push(failFn);//添加到rejectList数组中
} else if (self.status === "resolve") {
successFn(self.resolveVal);
} else {
failFn(self.rejectVal);
}
}
retun this;//链式调用
}
var a1 = new Mpromise(function (resolve, reject) {
setTimeout(() => {
resolve("成功数据1");
}, 5000);
});
a1.then(function (successDate) {
console.log(`监听到的数据是1:${successDate}`);
})
.then(function (successDate) {
console.log(`监听到的数据是2:${successDate}`);
})
.then(function (successDate) {
console.log(`监听到的成功数据是3:${successDate}`);
});
考虑到异步的情况,我们要采用队列的形式定义2个数组resolveList 和 rejectList 用来存储成功或失败后的回调,看代码,a1在创建完就马上执行then()方法,然后把回调函数添加到resolveList 中存储, 5秒后执行success函数,在把队列中的回调函数逐个拿出来调用,应此这里的返回的值应该是
但是,promise的函数远不止链式调用自身,因为这样没什么意义,根据es6中promise的描述
下一章试着实现 then的链式调用。
谢谢观赏,如有不足,请下方留言,共勉之! —一个菜鸡前端的路程