Promise
规范有不少,如Promise/A,Promise/B,Promise/D
以及Promise/A
的升级版Promise/A+
。ES6 中采用了Promise/A+
规范。 什么是 Promise/A+ 规范,推荐一篇文章Promises/A+规范(中文);jquery
1.一个promise
的当前状态只能是pending
、fulfilled
和rejected
三种之一。状态改变只能是pending
到fulfilled
或者pending
到rejected
,而且状态改变不可逆的。 2.promise
的then
方法接收两个可选参数,表示该promise
状态改变时的回调(promise.then(onFulfilled, onRejected
))。then
方法返回一个promise
。then
方法能够被同一个promise
调用屡次。数组
const promise = new Promise((resolve) => {
setTimeout(()=> {
resolve(1);
}, 2000);
});
promise.then(a=> alert(a));
复制代码
上面这段代码的解读:promise
executor
当即执行函数;executor
当即执行函数接收一个resolve
函数;promise
对象的then
方法绑定状态变为fulfilled
时的回调,then
方法也能够被称为注册函数(这句话怎么理解呢?不着急,下面会解释);resolve
函数被调用时会触发then方法中注册的回调函数(这句话很重要,在我看来,能理解这句话,就能理解promise
);用你们都会用,那么,咱们来实现一个最最最简单的promisebash
直接看下面这段代码异步
function MyPromise(executor){
var self = this;
this.status = "pending";
this.data = "";
this.resolveArr = [];
this.rejectArr = [];
function resolve(data){
//问题:为啥要在这里作异步处理(如过如今不能理解,等看完必定能理解的)
self.data = data
setTimeout(function(){
self.resolveArr.forEach((fn)=>{
fn(data);
});
},0);
}
function reject(){}
try{
executor(resolve,reject)
}catch(err){
reject(err)
}
}
MyPromise.prototype.then = function(resolveFn,rejectFn){
this.resolveArr.push(resolveFn);
this.rejectArr.push(rejectFn);
}
复制代码
来解释一下上边这段代码函数
MyPromise
接收了一个executor
做为参数,在实例化的时候去执行;这个executor
接收两个参数resolve
和reject
(注释:和原生的Promise(function(resolve,reject){}
)是保持一致的);测试
接下来看resolve
和reject
这俩参数,在MyPromise
函数体内,能够看到,这两个参数实际上是定义好的,能够理解为一个成功回调和一个失败的回调,和原生的Promise
保持一致(在接下来的讲解中,基本以成功回调的resolve
来作说明);来看下resolve
函数内部作了什么?发现resolve
只作了一件事,遍历了函数体内定义的resolveArr
,而后执行了resolveArr
了每一项,并把data
参数传递进去;优化
那么问题来了,既然resolveArr的每一项均可以执行,那就是函数咯,咱们能够看到,在函数体内,咱们只定义了一个空的resolveArr,那这个函数是怎么来的呢?ui
then
方法里面有用到这个数组(在文章的前面有说到,then
方法能够理解为注册方法,怎么个注册法呢?),原来在这个then
方法会接收两个函数类型的参数(和原生的Promise
保持一致);并把第一个参数添加到resolveArr
中,第二个参数添加到rejectArr
中。这也就完成了注册!说了那么多,来测试一下,是否有效:this
var test = new MyPromise(function(resolve){
setTimeout(()=>{
resolve("测试");
},1000);
});
test.then((data)=>{
console.log(data);
});
复制代码
运行结果以下:
重点再次提醒:时刻记住一句话,resolve
函数被调用时会触发then方法中注册的回调函数,从上面这个案例能够充分的体现;
理解了上面这个案例之后,咱们来慢慢的作优化
上面提到,then方法返回一个promise。then 方法能够被同一个 promise 调用屡次。来改写下then方法:
MyPromise.prototype.then = function(onResolved,onRejected){
this.resolveArr.push(onResolved);
this.rejectArr.push(onRejected);
return this;
}
复制代码
只须要在then
的最后return this
便可,看过jquery
源码的同窗应该都很熟悉吧! 问题点:这样作的话,全部then
方法注册的函数所接收的值都是同样的,这显然是咱们不能接收的,后面还会优化
上面提到,一个promise
的当前状态只能是pending
、fulfilled
和rejected
三种之一。状态改变只能是pending
到fulfilled
或者pending
到rejected
。来改下函数体内的resolve
方法:
function resolve(data){
if(self.status==="pending"){
self.status = "fulfilled";
setTimeout(function(){
self.resolveArr.forEach((fn)=>{
fn(data);
});
},0)
}
}
复制代码
只需在遍历resolveArr
前判断状态是否为pending
,若是是,则改变状态为fulfilled
,而后执行遍历,这里也解释了文章前面提到的一句话(promise
对象的then
方法绑定状态变为fulfilled
时的回调);
针对优化一提出的问题,来作进一步的优化
文章开头对promise/A+规范的标准解读中提到(then
方法返回一个promise
),那咱们就给then
方法返回一个promise,来改写then
方法:
MyPromise.prototype.then = function(onResolved,onRejected){
var self = this;
return new MyPromise(function(resolve,reject){
self.resolveArr.push(function(){
var x = onResolved(self.data);
if(x instanceof MyPromise){
//当then返回的是一个MyPromise的时候,会把resove挂在到该MyPromise的resolveArr队列中,等待该MyPromise执行对应的resolve;
x.then(resolve,reject)
}else{
//当then的返回值不是一个MyPromise的时候,直接执行resolve跳到下一个then处理;
resolve(x);
}
});
});
}
复制代码
再次强调:resolve
函数被调用时会触发then方法中注册的回调函数
提问:这里涉及了几个MyPromise
实例?
答案:2个或者3个;
为何呢?
来看一下,代码中有一句var x = onResolved(self.data)
,并在后面判断了x
是否是属于MyPromise
类型;从代码中也能够看出,x
是函数onResolved
的返回结果,那这个onResolved
又是什么呢?原来就是调用then
方法时所注册的函数;当x
也是一个MyPromise
实例的时候,这里就涉及了三个MyPromise
实例,分别是(当前实例(this
),then
方法返回的实例,注册函数返回的实例(x
));
分析
为了方便,我把当前实例称为A
,then
方法返回的实例称为B
; 当A
调用then
方法的时候,直接返回了B
,并在B
初始化的时候,给A
的resolveArr
加入一个匿名函数(这里记为AResolveFn
),当A
中的resolve
执行的时候,会去执行这个AResolveFn
,在这个AResolveFn
中,会去执行咱们传入then
中的onResolved
方法,并把返回结果记x
。
状况一:当x不为MyPromise
从代码中能够看出,在这种状况下,直接执行了resolve(x)
;这个resolve
是B
的resolve
,那B
的resolve
执行了,就会触发B
实例用then
方法注册的方法,这样就实现了then
的链式调用,而且把每次注册方法的返回值传下去啦!
测试:
var test = new MyPromise(function(resolve){
setTimeout(()=>{
resolve("测试");
},1000);
});
test.then((data)=>{
console.log(data);
return "测试2"
}).then((data)=>{
console.log(data);
});
复制代码
测试结果:
状况二:当x为MyPromise
从代码上看,当x
为MyPromise
的时候,直接把B
的resolve
当作了x
的注册方法;这样的话,只有当x
的resolve
执行的时候,会触发x
用then
注册的方法,才会触发B
的resolve
,才会触发B
用then
注册的方法;
流程就是:A
的resolve
--> A
用then
注册的方法 --> x
的resolve
--> x
用then
注册的方法 --> B
的resolve
--> B
用then
注册的方法,这样就实现了异步链式传递;
测试:
var test = new MyPromise(function(resolve){
setTimeout(()=>{
resolve("测试");
},1000);
});
test.then((data)=>{
console.log(data);
return new MyPromise(function(resolve){
setTimeout(()=>{
resolve("测试2");
},1000)
})
}).then((data)=>{
console.log(data);
});
复制代码
测试结果:
以上说到的都是正常的状态下;就是说,当你用then方法注册函数的时候,都是在pending状态;可是,有的特殊场景,在你用then
方法的时候,状态已是fulfilled
,就像下面这样:
var test = new MyPromise(function(resolve,reject){
setTimeout(function(){
resolve("test")
},1000);
});
setTimeout(function(){
test.then(function(data){
console.log(data);
});
},2000)
复制代码
在这种状况下,当调用resolve
方法的时候,并无用then方法注册函数,那么resolveArr
必然是一个空数组;此时状态是fulfilled
,不可能说你用then
方法注册的时候,再给你执行一边,也违背了Promise
的标准。
MyPromise.prototype.then = function(onResolved,onRejected){
var self = this;
if(self.status==="fulfilled"){
return new MyPromise(function(resolve,reject){
var x = onResolved(self.data);
if(x instanceof MyPromise){
x.then(resolve,reject)
}else{
resolve(x);
}
});
}
if(self.status==="pending"){
return new MyPromise(function(resolve,reject){
self.resolveArr.push(function(){
var x = onResolved(self.data);
if(x instanceof MyPromise){
x.then(resolve,reject)
}else{
resolve(x);
}
});
});
}
}
复制代码
在then
方法中作了一层判断,当状态是fulfilled
的时候,不会执行resolve
,固然也不会触发then
注册的函数,这里的作法是跳过这一层,直接去执行onResolved
,而后继续走下去;上面原理都理解的话,这里理解起来应该也不是特别困难,这里就不作过多解释了,实在理不清楚的话就留言给我吧!
原理到这里基本上就差很少了,这里再补充一个promise静态方法
Promise
中有许多静态方法,像Promise.all
,Promise.race
,那咱们用以上本身实现的MyPromise
来加一个静态方法试试:
MyPromise.all = function(promiseArr){
return new MyPromise(function(resolve){
var length = promiseArr.length;
var resultIndex = 0;
var resultArr = [];
promiseArr.forEach((promise)=>{
promise.then(function(data){
resultArr.push(data);
resultIndex++;
if(resultIndex===length){
resolve(resultArr);
}
});
});
});
}
复制代码
原理理解的话,这个实现起来也就不难了,这里就不讲解了,直接来测试一下吧:
var test1 = new MyPromise(function(resolve){
setTimeout(function(){
resolve("test1")
},1000)
});
var test2 = new MyPromise(function(resolve){
setTimeout(function(){
resolve("test2")
},1500)
});
var test3 = new MyPromise(function(resolve){
setTimeout(function(){
resolve("test3")
},1200)
});
复制代码
结果以下:
关于promise
的内容到这里就差很少了,平时也比较少发表文章,总怕本身总结的很差,但愿能给你们带来帮助吧!