面试之promise源码解析及应用

193 阅读2分钟

promise解决了什么问题

解决了回调地狱问题, 可以链式调用, 另外提供了Promise.all Promise.race Promise.resolve Promise.reject等方法

1. promise A+规范源码

    class myPromise {
      constructor(executor) {
        this.status = "pending";
        this.value = undefined; // 成功的data
        this.reason = undefined; // 失败data
        this.onResolvedCallbacks = []; // 成功的回调
        this.onRejectCallbacks = []; // 失败的回调
        let resolve = data => {
          if (this.status === "pending") {
            this.status = "resolve";
            this.value = data;
            this.onResolvedCallbacks.forEach(fn => fn()); // 依次执行成功的回调函数
          }
        };
        let reject = data => {
          if (this.status === "pending") {
            this.status = "reject";
            this.reason = data;
            this.onRejectCallbacks.forEach(fn => fn());
          }
        };
        try {
          executor(resolve, reject); // 函数传参为resolve, reject
        } catch (e) {
          reject(e);
        }
      }
      then(onFufilled, onRejected) {
        onFufilled = typeof onFufilled === "function" ? onFufilled : y => y;
        onRejected =
          typeof onRejected === "function"
            ? onRejected
            : err => {
                throw err;
              };
        let promise2;
        if (this.status === "resolve") {
          promise2 = new Promise((resolve, reject) => {
            setTimeout(() => {
              try {
                let x = onFufilled(this.value);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            }, 0);
          });
        }
        if (this.status === "reject") {
          promise2 = new Promise((resolve, reject) => {
            setTimeout(() => {
              try {
                let x = onRejected(this.reason);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            }, 0);
          });
        }
        if (this.status === "pending") {
          promise2 = new Promise((resolve, reject) => {
            this.onResolvedCallbacks.push(
              setTimeout(() => {
                try {
                  let x = onFufilled(this.value);
                  resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                  reject(e);
                }
              })
            );
            this.onRejectCallbacks.push(
              setTimeout(() => {
                try {
                  let x = onRejected(this.reason);
                  resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                  reject(e);
                }
              })
            );
          });
        }
      }
      resolvePromise(promise2, x, resolve, reject) {
        //判断x和promise2之间的关系
        //因为promise2是上一个promise.then后的返回结果,所以如果相同,会导致下面的.then会是同一个promise2,一直都是,没有尽头
        if (x === promise2) {
          //相当于promise.then之后return了自己,因为then会等待return后的promise,导致自己等待自己,一直处于等待
          return reject(new TypeError("循环引用"));
        }
        //如果x不是null,是对象或者方法
        if (x !== null && (typeof x === "object" || typeof x === "function")) {
          //为了判断resolve过的就不用再reject了,(比如有reject和resolve的时候)
          let called;
          try {
            //防止then出现异常,Object.defineProperty
            let then = x.then; //取x的then方法可能会取到{then:{}},并没有执行
            if (typeof then === "function") {
              //我们就认为他是promise,call他,因为then方法中的this来自自己的promise对象
              then.call(
                x,
                y => {
                  //第一个参数是将x这个promise方法作为this指向,后两个参数分别为成功失败回调
                  if (called) return;
                  called = true;
                  //因为可能promise中还有promise,所以需要递归
                  resolvePromise(promise2, y, resolve, reject);
                },
                err => {
                  if (called) return;
                  called = true;
                  //一次错误就直接返回
                  reject(err);
                }
              );
            } else {
              //如果是个普通对象就直接返回resolve作为结果
              resolve(x);
            }
          } catch (e) {
            if (called) return;
            called = true;
            reject(e);
          }
        } else {
          //这里返回的是非函数,非对象的值,就直接放在promise2的resolve中作为结果
          resolve(x);
        }
      }
      catch(onRejected) { // catch方法
        this.then(null, onRejected);
      }
    }

2.Promise.resolve

    Promise.resolve = function(data) {
      return new Promise((resolve, reject) => {
        resolve(data);
      });
    };

3. Promise.reject

    Promise.reject = function(data) {
     return new Promise((resolve, reject) => {
       reject(data);
     });
   };

4. Promise.all

  • 获取所有的promise, 如果全部resolve, 执行then方法, 如果有一个reject,执行catch
   Promise.all = function(promise) {
      let arr = [];
      let i = 0;
      function promiseArr(index, data) {
        arr[index] = data;
        i++;
        if (arr.length === i) {
          resolve(arr);
        }
      }
      return new Promise((resolve, reject) => {
        for (let i = 0; i < promise.length; i++) {
          promise[i].then(data => {
            promiseArr(i, data);
          }, reject);
        }
      });
    };

5.Promise.race

  • 哪个先执行完, 直接从then中取值
    Promise.race = function(promise) {
      return new Promise((resolve, reject) => {
        for (let i = 0; i < promise.length; i++) {
          promise[i].then(resolve, reject);
        }
      });
    };

6. promise 应用( 使用promise封装ajax)

    function ajaxPromise(
      url,
      methods = "GET",
      dataType = "JSON",
      async = true
    ) {
      return new Promise((resolve, reject) => {
        let XHR = new XMLHttpRequest();
        XHR.open(url, methods, async); // 打开方式, url
        XHR.responseType = dataType; // 返回文件格式
        XHR.onreadystatechange = function() {
          if (!/^[23]\d{2}$/.test(XHR.status)) return; // 不是2,3开头三位数字的状态吗, 退出函数
          if (XHR.readyState === 4) {
            let data = XHR.responseText; // 返回数据
            resolve(data);
          }
        };
        XHR.onerror = function(e) {
          // 异常处理
          reject(e);
        };
        XHR.send(); // 发送请求
      });
    }