相信promise这个单词大家都不陌生,就是诺言、承诺的意思。当一个人说承诺做一件事的时候,那么就会有三种状态,要么成功,要么失败,要么就是还在兑现诺言这个过程中。
因此,最基本的promise写法中,就有会实例化一个promise,且有一个基本的then方法,then方法中传入两个回调函数,一个返回成功,一个返回失败。在promise中,默认的状态是等待态(pending),一旦成功(resolve()),就不会失败(reject()),一旦失败,也不会成功。代码如下:
let p = new Promise(function(){
resolve('成功'); //调用成功态 成功
}
p.then(function(data){ //成功的回调
console.log(data);
},function(err){ //失败的回调
console.log(err);
})
promise基本实现
成功和失败
要实现上面代码中的功能,也是promise最基本的功能。首先,需要创建一个构造函数promise,在使用的时候传入了一个执行器executor,executor会传入两个参数:成功(resolve)和失败(reject)。之前说过,只要成功,就不会失败,只要失败就不会成功。所以,默认状态下,在调用成功时,就返回成功态,调用失败时,返回失败态。代码如下:
let promise = function Promise(executor){
let self = this; //保存this
self.status = 'pending'; //默认状态是pending,既不成功也不失败
self.value = undefind; //成功态的默认值
self.reason = undefind; //失败态的默认值
//一会解释为何定义这两个数组
self.resolvedArr = [];
self.rejectedArr = [];
//成功方法
function resolve(value){
if(self.status ==='pending'){
self.status = 'resolved';
self.value = value;
//一会解释这句代码的意义
self.resolvedArr.forEach(function(fn){
fn();
})
}
}
//失败方法
function reject(reason){
if(self.status === 'pending'){
self.status = 'rejected';
self.reason = 'reason';
//一会解释这句代码的意义
self.rejectedArr.forEach(function(fn){
fn();
})
}
}
executor(resolve,reject);
}
promise A+规范规定,在有异常错误时,则执行失败函数。
则上方代码应改为:
let promise = function Promise(executor){
......
try{
executor(resolve,reject);
}catch(e){
reject(e);
}
}
then方法
then方法是promise的最基本的方法,返回的是两个回调,一个成功的回调,一个失败的回调,实现过程如下:
promise.prototype.then = function(onFulfilled,onRejected){
let self = this;
if(self.status==='resolved'){ //成功状态时的调用
onFulfilled(self.value);
}
if(self.status==='rejected'){ //失败状态时的调用
onRejected(self.reason);
}
}
Promise的高阶用法
多次调用then
在promise规范中,规定可以多次调用then,返回多次结果。
使用resolve()来举例:
let p = new Promise(function(){
resolve('已经成功啦');
})
p.then(function(data){console.log(data);},function(err){});
p.then(function(data){console.log(data);},function(err){});
p.then(function(data){console.log(data);},function(err){});
返回的结果
已经成功啦
已经成功啦
已经成功啦
为了实现这样的效果,则上一次的代码将要重新写过,我们可以把每次调用resolve的结果存入一个数组中,每次调用reject的结果存入一个数组。这就是为何会在上面定义两个数组,且分别在resolve()和reject()遍历两个数组的原因。因此,在调用resolve()或者reject()之前,我们在pending状态时,会把多次then中的结果存入数组中,则上面的代码会改变为:
promise.prototype.then = function(onFulfilled,onRejected){
let self = this;
if(self.status==='resolved'){ //成功状态时的调用
onFulfilled(self.value);
}
if(self.status==='rejected'){ //失败状态时的调用
onRejected(self.reason);
}
//依次将resolve()和reject()的结果分别存入数组
if(self.status==='pending'){
self.resolvedArr.push(function(){ //将返回的每个成功的结果存入数组
onFulfilled(self.value);
})
self.rejectedArr.push(function(){ //将返回的每个失败的结果存入数组
onRejected(self.reason);
})
}
}
后面为节省时间我将只举例resolve()中代码的变化,reject()与resolve()的分析是一样的。
链式调用
Promise A+规范中规定then方法可以链式调用
在promise中,要实现链式调用返回的结果是返回一个新的promise,第一次then中返回的结果,无论是成功或失败,都将返回到下一次then中的成功态中,但在第一次then中如果抛出异常错误,则将返回到下一次then中的失败态中
链式调用失败时
举例如下:
let p = new Promise(function(){
reject('失败');
})
p.then(function(data){
},function(err){
console.log(err);
return '我是返回值';
}).then(function(data){
console.log(data); //我是返回值
},function(){
})
因此要实现链式调用,则需要定义返回的结果为一个新的promise,只要是promise,就会有两个状态,成功态和失败态,根据之前代码,实例化一个promise,传入resolve和reject参数。则上面的代码将被改变为:
promise.prototype.then = function(onFulfilled,onRejected){
let self = this;
let promise2; //表示返回新的promise
if(self.status==='resolved'){ //成功状态时的调用
pormise2 = new Promise(function(resolve,reject){
try{
onFulfilled(self.value);
}catch(e){
reject(e);
}
})
}
if(self.status==='rejected'){ //失败状态时的调用
promise2 = new Promise(function(resolve,reject){
try{
onRejected(self.reason);
}catch(e){
reject(e);
}
})
}
//依次将resolve()和reject()的结果分别存入数组
if(self.status==='pending'){
self.resolvedArr.push(function(){
promise2 = new Promise(function(resolve,reject){
try{
onFulfilled(self.value);
}catch(e){
reject(e);
}
})
})
self.rejectedArr.push(function(){
promise2 = new Promise(function(resolve,reject){
try{
onRejected(self.reason);
}catch(e){
reject(e);
}
})
})
}
return promise2;
}
链式调用成功时
链式调用成功会返回值,有多种情况,根据举的例子,大致列出可能会发生的结果。因此将链式调用返回的值单独写一个方法。方法中传入四个参数,分别是p2,x,resolve,reject,p2指的是上一次返回的promise,x表示运行promise返回的结果,resolve和reject是p2的方法。则代码写为:
function resolvePromise(p2,x,resolve,reject){
....
}
- 返回结果不能是自己
举例如下:
var p = new Promise(function(resovle,reject){
return p; //返回的结果不能是自己,
})
当返回结果是自己时,永远也不会成功或失败,因此当返回自己时,应抛出一个错误
function resolvePromise(p2,x,resolve,reject){
if(px===x){
return reject(new TypeError('循环引用'));
}
....
}
- 返回结果可能是promise
当是promise时,有可能是别人写的promise,promise可能是一个对象,或者函数,考虑各种可能。若是别人写的promise,里面的then,别人可能直接赋予一个常量,如:then:1,但then是一个函数,因此需要判断,当then满足是函数时,则调用then方法。如果是一个普通值(then:1),则直接返回(resolve())。 代码如下
function resolvePromise(p2, x, resolve, reject) {
// 看x是不是一个promise,如果是,则promise应该是一个对象
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {//当x为promise时
// 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
try { // {then:1}
let then = x.then;
if (typeof then === 'function') {
// 这里的then可能是全局上的then,因此,需让它定位是属于x的then,则用call
then.call(x, function (y) {
if (called) return
called = true
// y可能还是一个promise,在去解析直到返回的是一个普通值
resolvePromise(promise2, y, resolve, reject)
}, function (err) { //失败
reject(err);
})
} else {
resolve(x)
}
} catch (e) {
reject(e);
}
} else { // 说明是一个普通值1
resolve(x); // 表示成功了
}
}
promise.prototype.then = function(onFulfilled,onRejected){
let self = this;
let promise2; //表示返回新的promise
if(self.status==='resolved'){ //成功状态时的调用
pormise2 = new Promise(function(resolve,reject){
try{
let x = onFulfilled(self.value); //将返回的结果赋值给x,x有可能是普通值,也有可能是promise
//在resolvePromise中传入四个参数,第一个是返回的promise,第二个是返回的结果,第三个和第四个分别是resolve()和reject()的方法。
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
})
}
......
}
如果返回的结果是一个普通的值,
- 返回结果可能为一个普通值,则直接
resolve(x);
- Promise一次只能调用成功或者失败
也就是当调用成功就不能再调用失败了,如果两个都调用的时候,哪个先调用就执行哪一个。
function resolvePromise(p2,x,resolve,reject()){
let called; // 表示是否调用过成功或者失败
if(x !== null && (typeof x === 'object' || typeof x === 'function'){
try{
let then = x.then;
if (typeof then === 'function') {
then.call(x, function(y){
......
},function(err){
//预防同时调用成功或失败
if (called) return;
called = true;
......
})
}
}catch(e){
//预防同时调用成功或失败
if (called) return
called = true;
......
}
}else{
......
}
}
个人认为,这个地方比较绕,需要慢慢的一步一步的理清楚。
Promise A+规范的其他规定
promise中值的穿透
then链式调用多次空且最后一次传值时 p.then().then().then(function(){},function(){})
Promise.prototype.then = function(onFulfilled,onRejected){
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:function(value){
return value;
}
onRejected = typeof onRjected === 'function'?onRejected:function(err){
throw err;
}
......
}
根据promise A+规范原理,promise在自己的框架中,封装了一系列的内置的方法。
- 捕获错误的方法 Promise.prototype.catch()
- 解析全部方法 Promise.all()
- 竞赛 Promise.race()
- 生成一个成功的promise Promise.resolve()
- 生成一个失败的promise Promise.reject()
最后调用自己写的框架时,需加上这一句代码
module.exports = Promise;
关于这篇promise A+规范的总结,肯定会存在很多不足的地方,欢迎各位提出宝贵的意见或建议,也希望能帮助到你从中获得一些知识!