阅读 150

Promise —3分钟掌握最基本的Promise原理

前言

学习是一个漫长的过程,需要循序渐进,要想深入的理解一门技术,需要沉下心来慢慢研究。要做到通过现象看本质,学Promise的同时也会用到高阶函数和发布订阅,只有掌握了那些通用的技术,才能够更有效的学习下去,更好的看到本质。慢慢的你会发现通过深入学习一个Promise,收获的却不只是Promise。

一、什么是Promise

Promise 是异步编程的一种解决方案,Promise对象是一个构造函数,用来生成Promise实例.

二、Promise对象有以下两个特点。

1、有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败) 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态

2、一旦状态改变,就不会再变,Promise对象的状态改变只有两种可能: 从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果

三、Promise的实现(ES5)

1、名词解释:

1、executor:为执行器当new Promsie(executor)的时候,executor作为同步代码立即执行。

2、status:Promise实例的三个状态pending(进行中)、fulfilled(已成功)和rejected(已失败)

3、value:这个是成功传递的参数,成功有成功的值。

4、reason:这个是失败时候传递的参数,失败有失败的原因。

5、resolve:一个成功时调用的函数,作为参数传递给executor,为高阶函数

6、reject:一个失败时调用的函数,作为参数传递给executor,为高阶函数

2、最基础的Promise:

最基础的promise源码只有34行,看懂了这34行代码,你就懂得了promise的最核心部分,是不是很激动!!!

代码解读:
在Promise当中,定义好状态,成功的值,和失败的原因,当你传入
function(resolve,reject){resolve('成功了')}的时候resolve作为回调函数,
被触发执行了,然后之前定义的状态pending(等待)变成了fulfilled(成功),
此时调用then方法传入两个callback,通过内部比对if来执行onFulfilled或
onRejected。

function Promise(executor){
   const self = this;
   self.status = 'pending';//promise 状态
   self.value = undefined;// 成功有成功的值
   self.reason = undefined;// 失败有失败的原因

   function resolve(value){
       if(self.status === 'pending'){ //只有pending状态下才可以改变状态
           self.status = 'fulfilled';
           self.value = value;
       }
   }
   function reject(reson){
       if(self.status === 'pending'){//只有pending状态下才可以改变状态
           self.status = 'rejected';
           self.reason = reson;
       }
   }
   try{
       executor(resolve,reject); // 此处为高阶函数,一个函数内传入其他函数,这就是高阶函数的一种
   }catch(err){
       reject(err);
   }
}

Promise.prototype.then = function(onFulfilled,onRejected){
       const self = this;
       if(self.status === 'fulfilled'){
           onFulfilled(self.value);
       }
       if(self.status === 'rejected'){
           onRejected(self.reason);
       }
}


复制代码

上述代码可以正常运行以下代码:

const p = new Promise((resolve,reject)=>{
    resolve('成功') 
    // throw 1213
    // reject('失败')
});

p.then(function(data){
    console.log(data) // 成功
},function(err){
    console.log(err) // 失败 或 1213
})
复制代码

到这里最基本的Promise已经封装完成了,是不是很简单。

3、如何解决异步调用?

上面封装的代码如果遇到下面的代码是无结果的。

  • 同步代码的执行顺序是:executor ---> resolve ---> then
  • 异步代码的执行顺序是:executor ---> then ---> resolve

所以此时调用then方法的时候,记得看then方法的实现哦,他的内部status 状态是pending,回想一下之前同步的实现,到这里会有成功或者失败的状态,但这次是异步的情况,resolve要在then之后执行,所以这里要用到发布订阅模式(可以理解为,把callback装进数组中就是订阅,当循环数组执行的时候就是发布),对应的关系就是onResolvedCallbacks装着then方法成功的回调函数onFulfilledonRejectedCallbacks装着then方法失败的回调函数onRejected,当回调调用resolve的时候循环执行就行了

const p = new Promise((resolve,reject)=>{
   setTimeout(()=>{
       resolve('我是异步的成功')
   })      
});
复制代码

为了解决异步更新代码如下:

function Promise(executor){
    const self = this;
    self.status = 'pending';//promise 状态
    self.value = undefined;// 成功有成功的值
    self.reason = undefined;// 失败有失败的原因
+    self.onResolvedCallbacks = [];
+    self.onRejectedCallbacks = [];
 
    function resolve(value){
        if(self.status === 'pending'){ //只有pending状态下才可以改变状态
            self.status = 'fulfilled';
            self.value = value;
+           self.onResolvedCallbacks.forEach(item => item(value));
        }
    }
    function reject(reson){
        if(self.status === 'pending'){//只有pending状态下才可以改变状态
            self.status = 'rejected';
            self.reason = reson;
+           self.onRejectedCallbacks.forEach(item => item(value));
        }
    }
    try{
        executor(resolve,reject); // 此处为高阶函数,一个函数内传入其他函数,这就是高阶函数的一种
    }catch(err){
        reject(err);
    }
 }
 
 Promise.prototype.then = function(onFulfilled,onRejected){
        const self = this;
        if(self.status === 'fulfilled'){
            onFulfilled(self.value);
        }
        if(self.status === 'rejected'){
            onRejected(self.reason);
        }
+       if (self.status == 'pending') {
+           self.onResolvedCallbacks.push(function (value) {
+               try {
+                   onFulfilled(value);
+               } catch (e) {
+                   reject(e);
+               }
+           });
+           self.onRejectedCallbacks.push(function (value) {
+               try {
+                   onRejected(value);
+               } catch (e) {
+                   reject(e);
+               }
+           });
+       }
+ }
复制代码

4、Promise/A+完整实现

resolvePromisefang方法解决了那些事情:

resolvePromise是什么?:这其实是官方Promise/A+的需求。因为你的then可以返回任何职,当然包括Promise对象,而如果是Promise对象,我们就需要将他拆解,直到它不是一个Promise对象,取其中的值。

function Promise(executor) {
 let self = this;
 self.status = "pending";
 self.value = undefined;
 self.onResolvedCallbacks = [];
 self.onRejectedCallbacks = [];
 function resolve(value) {
+    if (value instanceof Promise) {
+      return value.then(resolve, reject)
+    }
+    setTimeout(function () { // 异步执行所有的回调函数
     if (self.status == 'pending') {
       self.value = value;
       self.status = 'resolved';
       self.onResolvedCallbacks.forEach(item => item(value));
     }
   });
+  }

 function reject(value) {
+    setTimeout(function () {
     if (self.status == 'pending') {
       self.value = value;
       self.status = 'rejected';
       self.onRejectedCallbacks.forEach(item => item(value));
     }
   });
+  }

 try {
   executor(resolve, reject);
 } catch (e) {
   reject(e);
 }
}
+ function resolvePromise(promise2, x, resolve, reject) {
+  if (promise2 === x) {
+    return reject(new TypeError('循环引用'));
+  }
+  let then, called;

+  if (x != null && ((typeof x == 'object' || typeof x == 'function'))) {
+    try {
+      then = x.then;
+      if (typeof then == 'function') {
+        then.call(x, function (y) {
+          if (called)return;
+          called = true;
+          resolvePromise(promise2, y, resolve, reject);
+        }, function (r) {
+          if (called)return;
+          called = true;
+          reject(r);
+        });
+      } else {
+        resolve(x);
+      }
+    } catch (e) {
+      if (called)return;
+      called = true;
+      reject(e);
+    }
+  } else {
+    resolve(x);
+  }
+}
Promise.prototype.then = function (onFulfilled, onRejected) {
 let self = this;
+  onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : function (value) {
+    return value
+  };
+  onRejected = typeof onRejected == 'function' ? onRejected : function (value) {
+    throw value
+  };
+  let promise2;
 if (self.status == 'resolved') {
+    promise2 = new Promise(function (resolve, reject) {
+      setTimeout(function () {
+        try {
+          let x = onFulfilled(self.value);
+          resolvePromise(promise2, x, resolve, reject);
+        } catch (e) {
+          reject(e);
+        }
+      });
+    });
 }
 if (self.status == 'rejected') {
+    promise2 = new Promise(function (resolve, reject) {
+      setTimeout(function () {
+        try {
+          let x = onRejected(self.value);
+          resolvePromise(promise2, x, resolve, reject);
+        } catch (e) {
+          reject(e);
+        }
+      });
+    });
 }
 if (self.status == 'pending') {
+    promise2 = new Promise(function (resolve, reject) {
+      self.onResolvedCallbacks.push(function (value) {
+        try {
+          let x = onFulfilled(value);
+          resolvePromise(promise2, x, resolve, reject);
+        } catch (e) {
+          reject(e);
+        }
+      });
+      self.onRejectedCallbacks.push(function (value) {
+        try {
+          let x = onRejected(value);
+          resolvePromise(promise2, x, resolve, reject);
+        } catch (e) {
+          reject(e);
+        }
+      });
+    });
+  }
+  return promise2;
}
复制代码

四、Promise其他方法的实现

all:

Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例

Promise.all=function(promises){
    return new Promise((resolve,reject)=>{
        promises=Array.from(promises);
        const length=promises.length, results=[],_index=0;
        if(length==0){
            resolve(results)
            return 
        }
        promises.forEach((promise,index)=>{
            Promise.resolve(promise).then(result=>{
                results[index]=result;
                if(length==++_index){
                   resolve(results)
                }
            }).catch(err=>{
                reject(err)
            })
        })
    })
}
复制代码

race

const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

Promise.race=function(promises){
    return new Promise((resolve,reject)=>{
        promises=Array.from(promises);
        if(promises.length==0){
            resolve()
            return 
        }
        promises.forEach((promise,index)=>{
            Promise.resolve(promise).then(result=>{
                resolve(result)
            }).catch(err=>{
                reject(err)
            })
        })
    })
}
复制代码

finally

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
复制代码

上面代码中,不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。

Promise.prototype.finally=function(onFinally){
    var isFunction = typeof onFinally == "function";
    return this.then(isFunction?(result)=>{
        onFinally();
        return result
    }:onFinally,isFunction?(err)=>{
        onFinally();
        return Promise.reject(err)
    }:onFinally);
}
复制代码

resolve/reject

resolve:有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用

reject:Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

//resolve方法
Promise.resolve = function(val){
  return new Promise((resolve,reject)=>{
    resolve(val)
  })
}
//reject方法

Promise.reject = function(val){
  return new Promise((resolve,reject)=>{
    reject(val)
  })
}
复制代码
关注下面的标签,发现更多相似文章
评论