最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Promise 源码分析

    正文概述 掘金(梦之云)   2021-05-29   676

    1、引入Promise

    • 字面意义"承诺",将来的某个时间进行兑现
    • 主要解决地域回调问题
    • Promise是异步的一种解决方案
    • Promise是一个构造函数,有属性和方法;通过new实例化使用

    2、Promise相关特点

    3、 源码解析

    步骤分析
    • 1、new MyPromise() 时自动触发executor函数(同步执行)
    • 2、调用then方法时,收集成功或失败函数(前提executor内异步执行)
    • 3、手动调用resolve或reject时,异步循环执行上一步收集的函数
    • 4、对上一步每次函数的执行结果放到resolvePromise函数中做处理
    源码实现
    const PENDING = "PENDING",
      FULFILLED = "FULFILLED",
      REJECTED = "REJECTED";
    
    
    class MyPromise {
      //executor
      ///1、自执行函数,new MyPromise时立刻执行
      ///2、包含两个参数:resolve和reject函数
      constructor(executor) {
    
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
    
        // 分别盛放所有的成功或失败的回调函数
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];
    
        // 不放到原型上:resolve和reject
        // 由于每次new的时候都需要创建自己的resolve和reject方法
        const resolve = (value) => {
          if (this.status == PENDING) {//状态只能被修改一次
            this.value = value;
            this.status = FULFILLED;
    
            //发布:迭代执行成功函数
            this.onFulfilledCallbacks.forEach(fn => fn());
          }
        }
        const reject = (value) => {
          if (this.status == PENDING) {//状态只能被修改一次
            this.reason = value;
            this.status = REJECTED;
    
            //发布:迭代执行失败函数
            this.onRejectedCallbacks.forEach(fn => fn());
          }
        }
    
        // 实例化时:executor函数内异常捕获处理 - 例如:throw new Error()
        try {
          // executor:此方法同步执行
          executor(resolve, reject);
        } catch (error) {
          reject(error)
        }
      }
    
      // 每个then都返回一个新的promie对象,进行链式调用
      then(onFulfilled, onRejected) {
    
        // 解决穿透问题:如果onFulfilled或onRejected为空,默认给一个函数
        // 例如:promise2.then().then().then((resolve,reject)=>{...})
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
        onRejected = typeof onRejected === "function" ? onRejected : (reason) => { throw reason };
    
        // 创建一个promise对象作为返回值
        let promise2 = new MyPromise((resolve, reject) => {
    
          // 以下代码使用setTimeout包裹:
          /// 1、使用setTimeout(宏任务)的处理方式,确保可以取到当前promise2的对象
          /// 2、源码中用的是微任务的处理方式
          /// 3、保障了多次then的链式调用,等待上次执行结束后才能执行下次( 事件循环机制 )
    
          // 下列代码res的值作为resolve和reject返回值
          /// 1、可能为普通值
          /// 2、可能为primise对象
          /// 3、使用resolvePromise方法进行处理
    
          // 同步代码执行executor时,且状态为FULFILLED
          if (this.status == FULFILLED) {
            setTimeout(() => {
              // 处理结果中抛出异常的情况,如:throw new Error()
              try {
                let res = onFulfilled(this.value)
                // 处理返回值的函数
                resolvePromise(promise2, res, resolve, reject)
              } catch (error) {
                reject(error);
              }
            }, 0);//具体延迟执行的时间>=4ms;
          }
    
          // 同步代码执行executor时,且状态为REJECTED
          if (this.status == REJECTED) {
            setTimeout(() => {
              try {
                let res = onRejected(this.reason)
                resolvePromise(promise2, res, resolve, reject)
              } catch (error) {
                reject(error);
              }
            }, 0);
          }
    
          // 异步代码执行executor时:收集所有的成功或失败的回调函数
          if (this.status == PENDING) {
            // 订阅:收集成功函数
            this.onFulfilledCallbacks.push(() => {
              setTimeout(() => {
                try {
                  let res = onFulfilled(this.value)
                  resolvePromise(promise2, res, resolve, reject)
                } catch (error) {
                  reject(error);
                }
              }, 0);
            })
            // 订阅:收集失败函数
            this.onRejectedCallbacks.push(() => {
              setTimeout(() => {
                try {
                  let res = onRejected(this.reason)
                  resolvePromise(promise2, res, resolve, reject)
                } catch (error) {
                  reject(error);
                }
              }, 0);
            })
          }
        })
    
        return promise2;
      }
    
      catch(errorCallback) {
        return this.then(null, errorCallback)
      }
    
      resolve(value) {
        return new MyPromise((resolve, reject) => {
          resolve(value);
        })
      }
    
      reject(reason) {
        return new MyPromise((resolve, reject) => {
          reject(reason);
        })
      }
    
      //所有实例执行成功才算成功
      all(promiseArray) {
        if (!Array.isArray(promiseArray)) {
          throw new TypeError(" The arguments should be an array!")
        }
        return new MyPromise((resolve, reject) => {
          try {
            const resultArray = [];
            const length = promiseArray.length;
    
            // 有一个实例失败直会接执行reject方法;
            //否则等待所有实例执行完执行resolve方法
            for (let i = 0; i < length; i++) {
              promiseArray[i].then(data => {
                resultArray.push(data);
                if (resultArray.length === length) {
                  resolve(resultArray)
                }
              }, reject)
            }
          }
          catch (e) {
            reject(e)
          }
        })
      }
    
      // 返回第一个改变状态的结果,无论成功的还是失败的
      race(promiseArray) {
        if (!Array.isArray(promiseArray)) {
          throw new TypeError(" The arguments should be an array!")
        }
        return new MyPromise((resolve, reject) => {
          try {
            const length = promiseArray.length;
            for (let i = 0; i < length; i++) {
              promiseArray[i].then(resolve, reject);
            }
          }
          catch (e) {
            reject(e)
          }
        })
      }
    }
    
    // 处理then中成功函数或失败函数的返回值
    function resolvePromise(promise2, res, resolve, reject) {
    
      // 如果promise2等于res,抛出异常
      // 例如:let promise1 = promise.then((value) => { return promise1; })
      if (promise2 == res) {
        return reject(new TypeError('Changing cycle detected for promise #<MyPromise>'))
      }
    
      // 如果res是一个object或function时
      if ((typeof res === "object" && res !== null) || (typeof res === "function")) {
        //保证res.then取值的时候出现异常
        try {
          let then = res.then;//判断是否为 promise 对象
    
          //判断回调函数是否被多次调用过:例如 - resolve();resolve();连续多次调用
          let called = false;
    
          //为promise对象
          if (typeof then === "function") {
            then.call(res, (y) => {
              // 避免重复调用
              if (called) return;
              called = true;
              // 如果Y为Promise对象,递归调用
              // 如果Y为普通值,相当于下次直接调用 - resolve()
              resolvePromise(promise2, y, resolve, reject)
            }, (r) => {
              // 避免重复调用
              if (called) return;
              called = true;
              reject(r)
            })
          }
          else {
            resolve(res);
          }
        } catch (error) {
          // 避免重复调用
          if (called) return;
          called = true;
          reject(error)
        }
      }
      //res为普通值
      else {
        resolve(res);
      }
    }
    
    module.exports = MyPromise;
    

    起源地下载网 » Promise 源码分析

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元