zoukankan      html  css  js  c++  java
  • Promise核心实现

    核心

    构造函数核心

    • 维护状态变量,只能由pending变为resolve或者reject
    • 维护一个存储结果的变量
    • 维护一个回调数组,执行到then,如果我们传入的立即执行函数没有立即执行resolve或者reject,所以promise的状态还是pending,这时要把then里面的回调函数保存起来。待到resolve或者reject执行后则执行回调数组里存到方法。若传入的立即执行函数直接执行了resolve或者reject此时就不用把回调保存起来,直接执行onResolved或onRejected方法。注意是异步执行。而且是做为微任务的。(下面我们用setTimeout模拟)

    then核心

    • 执行时,若当前状态为pending,则把回调保存到回调数组,若为resolve或者reject则直接异步执行(微任务)。如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。即resolve(result)或者reject(result)
    • 每次then都返回一个新的Promise。
    • promise会发生值传透,当then中传入的不算函数,则这个then返回的promise的data,将会保存上一个的promise.data。这就是发生值穿透的原因。而且每一个无效的then所返回的promise的状态都为resolved。

    核心实现

    将Promise向外暴露

    (function (window) {
        /*
        Promise构造函数
        executor:执行器函数
         */
        function Promise(executor) {
    
        }
    
        // 向外暴露Promise
        window.Promise = Promise
    })()

    构造函数实现

      function MyPromise(exector) {
        var self = this;
        self.status = 'pending'; // 给promise对象指定status属性,初始值为pending
        self.data = undefined; // 给promise对象指定一个存储结果的data
        self.callbacks = []; // 每个元素的结构 {onResolved(){},onRejected(){}}
        function resolve(value) {
          if(self.status !== 'pending') {
            return
          }
          self.status = 'resolved'; // 将状态更改为resolved
          self.data = value; // 保存value的值
          // 异步调用resolve才会在这里执行
          // 如果有待执行的callback函数,立即异步执行回调函数onResolved();
          if(self.callbacks.length > 0) {
            self.callbacks.forEach(callbacksObj => {
              callbacksObj.onResolved(value);
            });
          }
        }
    
        function reject() {
          // 如果当前状态不是pending,则不执行
          if(self.status !== 'pending'){
              return
          }
          // 将状态改为rejected
          self.status = 'rejected';
          // 保存value的值
          self.data = value;
          // 如果有待执行的callback函数,立即异步执行回调函数onResolved
          if (self.callbacks.length>0){
            self.callbacks.forEach(callbackObj=>{
                callbackObj.onRejected(value)
            })
          }
        }
        // 立即同步执行exector
        // 注意⚠️:当在执行executor的时候,如果执行异常的话,这个promise的状态会直接执行reject方法
        try{
          // 立即同步执行executor
          executor(resolve,reject);
        } catch (e) {
          // 如果执行器抛出异常,promise对象变为rejected状态
          reject(e);
        }
      }

    then实现

     MyPromise.prototype.then = function(onResolved, onReject) {
        var self = this;
        // 处理值穿透
        onResolved = typeof onResolved === 'function'? onResolved: value => value
        onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
        
        return new myPromise((resolve,reject) => {
          if(self.status === 'pending') {
            // promise当前状态还是pending状态,将回调函数保存起来
            self.callbacks.push({
              onResolved() {
                handle(onResolved)
              },
              onRejected() {
                handle(onRejected)
              }
            })
          } else if (self.status === 'resolved') {
            setTimeout(()=>{
              handle(onResolved)
            })
          } else { // 当status === 'rejected'
            setTimeout(()=>{
              handle(onRejected)
            })
          }
          // 处理函数
          function handle(callback) {
            try {
              const result = callback(self.data)
              if (result instanceof MyPromise){
                // 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
                result.then(
                  value => {resolve(value)},
                  reason => {reject(reason)}
                )
              } else {
                // 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
                resolve(result)
              }
            } catch (e) {
              //  如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
              reject(e)
            }
          }
        });
      }

    其他方法实现

    catch

    借用then方法

    MyPromise.prototype.catch = function(onRejected){
        return this.then(undefined,onRejected)
    }

    finally

    借用then方法

      MyPromise.prototype.finally = (onFinally) => {
        return this.then((res)=>{
          MyPromise.resolve(onFinally()).then(()=> res)
        },(reson)=>{
          MyPromise.resolve(onFinally()).then(()=> reson)
        })
      }

    resolve

    Promise参数可以为如下三种

    • 不是promise
    • 成功状态的promise
    • 失败状态的promise
    MyPromise.resolve = function(value){
      return new MyPromise((resolve,reject)=>{
          if (value instanceof Promise){
              // 如果value 是promise
              value.then(
                  value => {resolve(value)},
                  reason => {reject(reason)}
              )
          } else{
              // 如果value不是promise
              resolve(value)
          }
      }
    }

    reject

    MyPromise.reject = function(reason) {
        return new MyPromise((resolve,reject)=>{
            reject(reason)
        })
    }

    all

    • 入参一般是个由Promise实例组成的数组,但是也可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。若参数如果不是 Promise 实例,就会先调用Promise.resolve()方法,将参数转为 Promise 实例,再进一步处理。
    • 返回值是个promise,因为可以使用.then
    • 如果全部成功,状态变为resolved, 并且返回值组成一个数组传给回调
    • 但凡有一个失败,状态变为rejected, 并将error返回给回调
    MyPromise.all = (promisesArr) => {
        // 返回Promise
        return new MyPromise((resolve, reject) => {
            let dataArr = new Array(promisesArr.length);
            let count = 0;
            for (let i = 0; i < promisesArr.length; i++) {
                // 在 .then 中收集数据,并添加 .catch,在某一个 Promise 遇到错误随时 reject。
                // 这样,在最外面调用 Promise.all().catch() 时也可以 catch 错误信息
              // 判断当前这个元素是否为Promise对象,不是则转为Promise对象
                let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]);
                currentPromise.then(res => { 
                dataArr[index] = data;
                  count++;
                  // 如果数据收集完了,就把收集的数据 resolve 出去
                  if (count === promisesArr.length) resolve(dataArr);
            }).catch(err => { 
                //如果某一个失败,promise.all()立即执行reject回调。
                  //但剩余的promise依旧继续执行,只不过对promise.all的结果不会产生影响了
                reject(err) 
            });
            }
        })

    注意⚠️:dataArr添加时用下标而不用数组时为了防止顺序错乱

    race

    返回一个promise对象,状态由第一个完成的promise决定

    MyPromise.race = function(promisesArr){
        return new Promise((resolve,reject)=>{
            // 遍历promises,获取每个promise的结果
            for (let i = 0; i < promisesArr.length; i++) {
                // 判断当前这个元素是否为Promise对象,不是则转为Promise对象
                let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]);
                currentPromise.then(
                    value => {
                        // 只要有一个成功,返回的promise的状态九尾resolved
                        resolve(value)
                    },
                    reason => { //只要有一个失败,return的promise状态就为reject
                        reject(reason)
                    }
                )
            })
        })
    }

    allSettled

    Promise.allSettled() 方法返回一个在所有给定的 promise 已被决议或被拒绝后决议的 promise,并带有一个对象数组,每个对象表示对应的promise 结果。

    Promise.newAllSettled = function (promisesArr) {
      return new Promise((resolve, reject) => {
        let results = [];
        let count = 0;
        let promisesArrLength = promisesArr.length;
        // 运行所有的 Promise
        for (let i = 0; i < promisesArr.length; i++) {
          // 判断当前这个元素是否为Promise对象,不是则转为Promise对象
          let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]);
          currentPromise.then(res => {
            // 当有 Promise 被 resolve 之后,记录 resolve 值和状态,已决 Promise 计数加一
            results.push({value: res, status: 'fulfilled'});
            count++;
            // 全部 Promise 已决,resolve
            if (count === promisesArrLength) {
              resolve(results);
            }
          }).catch(err => {
            // 当有 Promise 被 reject 后,记录 reject 值和状态,并且已决的 Promise 计数加一
            results.push({value: err, status: 'rejected'});
            count++;
            if (count === promisesArrLength) {
              resolve(results);
            }
          });
        }
      })
    };

    参考

    https://juejin.im/post/6844904088963022856

    https://juejin.im/post/6850037281206566919

    https://blog.csdn.net/MichelleZhai/article/details/104475521

    https://zhuanlan.zhihu.com/p/60287801

    https://zhuanlan.zhihu.com/p/41502945

    https://zhuanlan.zhihu.com/p/61681036

    https://juejin.im/post/6856213486633304078

    https://zhuanlan.zhihu.com/p/107712626

  • 相关阅读:
    PHP 将二维数组中某列值作为数组的键名
    MySQL 8下忘密码后重置密码
    单一职责原则
    Linux下安装SVN服务端小白教程
    go 代码玩耍
    centos7 docker开启认证的远程端口2376配置教程
    Dockerfile RUN,CMD,ENTRYPOINT命令区别
    wait-for-it.sh脚本控制docker-compose启动顺序详解
    阿里云服务器漏洞修复_2020.5.22
    Let's Encrypt 免费通配符 SSL 证书申请教程
  • 原文地址:https://www.cnblogs.com/suihang/p/13656581.html
Copyright © 2011-2022 走看看