Skip to content

Promise ​

Promise的基本用法 ​

  1. Promise是一个构造函数,必须接受一个回调函数作为参数(resolve,reject作为参数)
    js
      let p = new Promise((resolve, reject) => {
        //dosomething  同步调用的
        resolve(n)  //n传给then
        //dosomething
        reject(n)
        //两者只会执行一个,一旦执行其中一个
        //promise的状态就会从pending转变成fulfilled或者rejected,并且无法改变
      })
  2. Promise在then方法中执行其对应的成功和失败回调
    js
        p.then((value)=>{ //value成功值
          //成功回调
        },(reason)=>{  //reason理由
          //失败回调
        })

使用promise封装原生sendAjax ​

  • js
        function sendAJAX(url){
          return new Promise((resolve,reject)=>{
            const xhr = new XMLHttpRequest()    
            xhr.open('GET',url)
            xhr.send()
            //处理结果    
            xhr.onreadystatechange = function(){
              if(xhr.readystate === 4){
                //判断    
                if(xhr.status >= 200 && xhr.status<=300){
                  resolve(xhr.response)
                }else{
                  reject(xhr.status)
                }
              }
            }
          })
        }

Promise的静态方法 ​

  1. Promise.resolve()
  • 有时需要将现有对象转成Promise 对象
    js
        Promise.resolve('foo')
        // 等价于
        new Promise(resolve => resolve('foo'))
  • 参数情况
    1. 如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例
    2. 参数是一个thenable对象,Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法,
      js
          let thenable = {
            then: function(resolve, reject) {
              resolve(42);
            }
          };
          let p1 = Promise.resolve(thenable);
          p1.then(function(value) {
            console.log(value);  // 42    
            //上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42
          });
    3. 参数不是具有then方法的对象,或根本就不是对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved
    4. 不带有任何参数,直接返回一个resolved状态的Promise对象
  1. Promise.reject(reason)
    js
        Promise.reject()    
        //无论传入什么,返回的都是rejected状态的promise对象   
        const p = Promise.reject('出错了');
        // 等同于
        const p = new Promise((resolve, reject) => reject('出错了'))
        p.then(null, function (s) {
          console.log(s)
        });
        // 出错了
  2. Promise.all()
  • Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
    js
          const p = Promise.all([p1, p2, p3]);
          //1.Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理    
          //2.Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例
          
          //3.p的状态由p1、p2、p3决定,分成两种情况   
          //(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数   
          //(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
  • 注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。,因为自己有catch方法,他首先会rejected,然后被自己的catch捕获,然后状态变成了resolved,这样就不会执行.all的catch方法,而是会执行then方法
    js
        const p1 = new Promise((resolve, reject) => {
          resolve('hello');
        })
        .then(result => result)
        .catch(e => e);
        const p2 = new Promise((resolve, reject) => {
          throw new Error('报错了');
        })
        .then(result => result)
        .catch(e => e);
        Promise.all([p1, p2])
        .then(result => console.log(result))
        .catch(e => console.log(e));
        // ["hello", Error: 报错了]
  1. Promise.race()
  • Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
    js
          //传入对象也是一个数组,谁先处理完,返回的状态就以谁为准    
          const p = Promise.race([
            fetch('/resource-that-may-take-a-while'),
            new Promise(function (resolve, reject) {
              setTimeout(() => reject(new Error('request timeout')), 5000)
            })
          ]);
          p
          .then(console.log)
          .catch(console.error);    
          //如果指定时间内没有获得结果,就将 Promise 的状态变为reject,否则变为resolve
  1. Promise.allSettled()
  • Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束
  1. Promise.any()
  • Promise.any()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态

Promise的实例方法 ​

  1. promise.prototype.then
  • then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
  • then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法.
  • 链式调用时,如果第一个then中回调函数返回了一个promise对象,那么后一个then就会等待其状态变化来决定执行对应回调函数
    js
        let p = new Promise((resolve,reject)=>resolve('OK'))    
        let result = p.then(value=>{
          //then的返回结果由传入的回调函数的返回值决定
          //1.抛出错误    
          //throw '出了问题'      
          //2. 返回结果是非Promise类型的对象    
          //return 521    
          //3.返回结果是Promise对象   
          //return new Promise((resolve,reject)=>{
            //resolve('success')
          //})
        },reason=>{
          console.warn(reason)
        })
  1. promise.prototype.catch
    • 可以理解成是.then(null, rejection)或.then(undefined, rejection)的别名
    • 建议总是使用catch()方法,而不使用then()方法的第二个参数
    • catch可以捕获在其之前抛出的错误,或者是状态为rejected的promise对象
  2. Promise.prototype.finally()
    • finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
    • finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected
    • 本质上就是一个then的特例
      js
          promise.finally(() => {
            // 语句
          });
          // 等同于
          promise.then(
            result => {
              // 语句
              return result;
            },
            error => {
              // 语句
              throw error;
            }
          );

手写Promise ​

实现思路:Promise源码实现 ​

  1. 模拟实现

    js
        // 模拟实现Promise
        // Promise利用三大手段解决回调地狱:
        // 1. 回调函数延迟绑定
        // 2. 返回值穿透
        // 3. 错误冒泡
    
        // 定义三种状态
        const PENDING = 'PENDING';      // 进行中
        const FULFILLED = 'FULFILLED';  // 已成功
        const REJECTED = 'REJECTED';    // 已失败
    
        class Promise {
          constructor(exector) {
            // 初始化状态
            this.status = PENDING;
            // 将成功、失败结果放在this上,便于then、catch访问
            this.value = undefined;
            this.reason = undefined;
            // 成功态回调函数队列
            this.onFulfilledCallbacks = [];
            // 失败态回调函数队列
            this.onRejectedCallbacks = [];
    
            const resolve = value => {
              // 只有进行中状态才能更改状态 保证状态只更改一次
              if (this.status === PENDING) {
                //1.修改对象的状态
                this.status = FULFILLED;
                //2.修改对象结果值
                this.value = value;
                // 成功态函数依次执行 
                this.onFulfilledCallbacks.forEach(fn => fn(this.value));
              }
            }
            const reject = reason => {
              // 只有进行中状态才能更改状态
              if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason;
                // 失败态函数依次执行
                this.onRejectedCallbacks.forEach(fn => fn(this.reason))
              }
            }
            try {
              // 立即执行executor  
              // 把内部的resolve和reject传入executor,用户可调用resolve和reject
              exector(resolve, reject);
            } catch(e) {
              // 对于抛出错误的情况,executor执行出错,将错误内容reject抛出去
              reject(e);
            }
          }
          then(onFulfilled, onRejected) {
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
            onRejected = typeof onRejected === 'function'? onRejected :
              reason => { throw new Error(reason instanceof Error ? reason.message : reason) }
            // 保存this
            const self = this;
            return new Promise((resolve, reject) => {
              if (self.status === PENDING) {
                self.onFulfilledCallbacks.push(() => {
                  // try捕获错误
                  try {
                    // 模拟微任务
                    setTimeout(() => {
                      const result = onFulfilled(self.value);
                      // 分两种情况:
                      // 1. 回调函数返回值是Promise,执行then操作
                      // 2. 如果不是Promise,调用新Promise的resolve函数
                      result instanceof Promise ? result.then(resolve, reject) : resolve(result);
                    })
                  } catch(e) {
                    reject(e);
                  }
                });
                self.onRejectedCallbacks.push(() => {
                  // 以下同理
                  try {
                    setTimeout(() => {
                      const result = onRejected(self.reason);
                      // 不同点:此时是reject
                      result instanceof Promise ? result.then(resolve, reject) : resolve(result);
                    })
                  } catch(e) {
                    reject(e);
                  }
                })
              } else if (self.status === FULFILLED) {
                try {
                  setTimeout(() => {
                    const result = onFulfilled(self.value);
                    result instanceof Promise ? result.then(resolve, reject) : resolve(result);
                  });
                } catch(e) {
                  reject(e);
                }
              } else if (self.status === REJECTED) {
                try {
                  setTimeout(() => {
                    const result = onRejected(self.reason);
                    result instanceof Promise ? result.then(resolve, reject) : resolve(result);
                  })
                } catch(e) {
                  reject(e);
                }
              }
            });
          }
          catch(onRejected) {
            return this.then(null, onRejected);
          }
          static resolve(value) {
            if (value instanceof Promise) {
              // 如果是Promise实例,直接返回
              return value;
            } else {
              // 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED
              return new Promise((resolve, reject) => resolve(value));
            }
          }
          static reject(reason) {
            return new Promise((resolve, reject) => {
              reject(reason);
            })
          }
          static all(promiseArr) {
            const len = promiseArr.length;
            const values = new Array(len);
            // 记录已经成功执行的promise个数
            let count = 0;
            return new Promise((resolve, reject) => {
              for (let i = 0; i < len; i++) {
                // Promise.resolve()处理,确保每一个都是promise实例
                Promise.resolve(promiseArr[i]).then(
                  val => {
                    values[i] = val; //不用push 防止成功的顺序不一致
                    count++;
                    // 如果全部执行完,返回promise的状态就可以改变了
                    if (count === len) resolve(values);
                  },
                  err => reject(err),
                );
              }
            })
          }
          static race(promiseArr) {
            return new Promise((resolve, reject) => {
              promiseArr.forEach(p => {
                Promise.resolve(p).then(
                  val => resolve(val),
                  err => reject(err),
                )
              })
            })
          }
        }
  2. Promise.all

    • Promise.all是支持链式调用的,本质上就是返回了一个Promise实例,通过resolve和reject来改变实例状态。
      js
          Promise.myAll = function(promiseArr) {
            return new Promise((resolve, reject) => {
              const ans = [];
              let index = 0;
              for (let i = 0; i < promiseArr.length; i++) {
                promiseArr[i]
                .then(res => {
                  ans[i] = res;
                  index++;
                  if (index === promiseArr.length) {
                    resolve(ans);
                  }
                })
                .catch(err => reject(err));
              }
            })
          }
  3. Promise.race

    js
       Promise.race = function(promiseArr) {
          return new Promise((resolve, reject) => {
            promiseArr.forEach(p => {
              // 如果不是Promise实例需要转化为Promise实例
              Promise.resolve(p).then(
                val => resolve(val),
                err => reject(err),
              )
            })
          })
        }
  4. Promise并行限制

    • 就是实现有并行限制的Promise调度器问题。
      js
          class Scheduler {
            constructor() {
              this.queue = [];
              this.maxCount = 2;
              this.runCounts = 0;
            }
            add(promiseCreator) {
              this.queue.push(promiseCreator);
            }
            taskStart() {
              for (let i = 0; i < this.maxCount; i++) {
                this.request();
              }
            }
            request() {
              if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
                return;
              }
              this.runCounts++;
      
              this.queue.shift()().then(() => {
                this.runCounts--;
                this.request();
              });
            }
          }
            
          const timeout = time => new Promise(resolve => {
            setTimeout(resolve, time);
          })
            
          const scheduler = new Scheduler();
            
          const addTask = (time,order) => {
            scheduler.add(() => timeout(time).then(()=>console.log(order)))
          }
            
            
          addTask(1000, '1');
          addTask(500, '2');
          addTask(300, '3');
          addTask(400, '4');
          scheduler.taskStart()
          // 2
          // 3
          // 1
          // 4