Promise学习笔记

参考视频教程

知识铺垫

函数对象和实例对象

  • 函数对象:将函数作为对象使用
  • 实例对象:new 函数产生的对象(执行构造函数)

只有在new出实例对象之后,函数才能叫做构造函数

function Person() {

}
var p = new Person();
console.log(Person.prototype)

在这段代码中p是实例对象,Person是构造函数,打印Person原型时,Person是函数对象

回调函数

  • 同步回调:立即执行,完全执行完了才结束,不会放入回调队列

  • 异步回调:不会立即执行,放入回调队列中将来执行

const arr = [1, 2, 3];
arr.forEach(item => {
  console.log(item);
});
console.log('foreach之后');


setTimeout(() => {
  console.log('callback');
}, 0);
console.log('setTime之后');

image-20200819110221552

上面一段是同步回调,按照顺序执行;下面一段是异步函数,放到队列中执行,主线程执行完毕之后才执行

错误处理

  • 错误的类型

    • Error: 所有错误的父类型
    • ReferenceError: 引用的变量不存在
    • TypeError: 数据类型不正确的错误
    • RangeError: 数据值不在其所允许的范围内
    • SyntaxError: 语法错误
  • 错误处理

    • 捕获错误: try … catch
    • 抛出错误: throw error
  • 错误对象

    • message属性: 错误相关信息
    • stack属性: 函数调用栈记录信息

Promise理解和使用

什么是Promise

  • 抽象表达:

    • Promise是JS中进行异步编程的新的解决方案(旧的是谁?纯回调callback)
  • 具体表达:

    • 从语法上来说: Promise是一个构造函数
    • 从功能上来说: promise对象用来封装一个异步操作并可以获取其结果
  • promise的状态改变(只有2种结果,,只能改变一次)

    • pending变为resolved
    • pending变为rejected
    • 无论成功还是失败都会有一个 结果,成功的称为value,失败的称为reason
  • promise的基本流程(pending,图里面写错了)

    image-20200819155202362

为什么使用Promise

  1. 指定回调函数的方式更加灵活:可以在请求发出甚至结束后指定回调函数
  2. 支持链式调用,可以解决回调地狱问题

怎么使用Promise

简单的示例

const p = new Promise((resolve, reject) => {
  const d = Date.now();
  if (d % 2 === 1) {
    resolve('success')
  } else {
    reject('faild')
  }
})
p.then(value => console.log(value), reason => console.log(reason))

.then()接收两个函数,第一个是成功的回调(onResolved),第二个是失败的回调(onRejected)

主要API

  • Promise构造函数: Promise (excutor) {}
  • Promise.prototype.then方法: (onResolved, onRejected) => {}
  • Promise.prototype.catch方法: (onRejected) => {},拦截失败操作,返回promise对象,如果catch之前有reject,直接跳到reject,中间所有的.then都不执行
  • Promise.resolve方法: (value) => {},修改状态为成功
  • Promise.reject方法: (reason) => {},修改状态为失败
  • Promise.all方法: (promises) => {},参数为一个可迭代对象,等所有的promise对象都完成之后状态变为resolved,返回结果可迭代对象;如果过有失败结果,状态变为rejected,失败原因是第一个失败的promise对象的结果
  • Promise.race方法: (promises) => {},参数为一个可迭代对象,如果有任何一个promise解决或者拒绝,返回的promise就会解决或者拒绝

存在的问题

  • 改变状态不一定是resolve和reject,也可以发生错误或者抛出异常
  • 同一个promise对象添加多个.then都会执行
  • 改变promise状态和指定回调函数谁先谁后
    • 正常情况下是先指定回调函数再改变状态
    • 先改变状态再指定回调:①在执行器中直接调用resolve()/reject()②延迟更长时间才调用then()
  • 什么时候得到数据
    • 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
    • 如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
  • 不论成功失败,回调函数永远是异步执行
  • .then返回promise的状态——由指定的回调函数执行的结果决定
    • 抛出异常,新的promise变为rejected,reason为抛出的异常
    • 返回的是非promise任意值,状态改为resolved,value是返回值
    • 返回值是一个新的promise,这个promise的结果就会成为新的promise的结果

自定义Promise

整体结构

由于ES6和CommenJS都需要编译才能使用,所以自定义Promise需要使用ES5模块语法(自调用函数)

(function (window) {
  /* 
  Promise:构造函数
  excutor: 执行器
  */
  function Promise(excutor) {

  }

  /* 
  Promise 原型对象.then
  指定成功和失败的回调函数
  返回一个新的Promise
  */
  Promise.prototype.then = function (onResolved, onRejected) {

  }

  /*
  Promise 原型对象.catch
  指定失败的回调函数
  返回一个新的Promise
  */
  Promise.prototype.catch = function (onRejected) {

  }

  /*
  Promise 原型对象.resolve
  返回一个指定value的Promise
  */
  Promise.resolve = function (value) {

  }

  /*
  Promise 原型对象.reject
  返回一个指定reason的Promise
  */
  Promise.reject = function (reason) {

  }

  /*
  Promise 原型对象.all
  返回一个Promise,所有promise都成功才成功,一个失败就失败
  */
  Promise.all = function (promises) {

  }

  /*
  Promise 原型对象.race
  返回一个Promise,由第一个完成的promise决定
  */
  Promise.race = function (promises) {

  }

  // 暴露Promise
  window.Promise = Promise;
})(window)

构造函数

/* 
Promise:构造函数
excutor: 执行器
*/
function Promise(excutor) {
  this.status = 'pending'; // 状态
  this.data = undefined;  // 数据
  this.callbacks = [];  // 保存回调函数,结构:{onResolved() {}, onReject() {}}
  function resolve(value) {
    // 当前状态不是pending结束
    if (this.status !== 'pending') {
      return
    }
    // 修改状态
    this.status = 'resolved';
    // 保存数据
    this.data = value;
    // 立即执行callback
    if (this.callbacks.length > 0) {
      this.callbacks.forEach(callbacksObj => {
        setTimeout(() => {  // 异步执行回调
          callbacksObj.onResolved(value)
        }, 0)
      });
    }

  }

  function reject(reason) {
    // 当前状态不是pending结束
    if (this.status !== 'pending') {
      return
    }
    // 修改状态
    this.status = 'rejected';
    // 保存数据
    this.data = value;
    // 立即执行callback
    if (this.callbacks.length > 0) {
      this.callbacks.forEach(callbacksObj => {
        setTimeout(() => {  // 异步执行回调
          callbacksObj.onRejected(reason)
        }, 0)
      });
    }
  }
  // 立即执行excutor
  try {
    excutor(resolve, reject);
  } catch (error) { // 抛出异常变为rejected
    reject(error);
  }
}

Promise.then()/.catch()

先来看一下这个例子,将fn赋值给f,两种方法,第二种比第一种简洁,在.then内部返回数据时会用到

function fn() {

}
var f = function fn() {

}
var f = fn;

正片开始

  /* 
  Promise 原型对象.then
  指定成功和失败的回调函数
  返回一个新的Promise
  */
  Promise.prototype.then = function (onResolved, onRejected) {
    onResolved = typeof onResolved === 'function' ? onResolved : value => value

    onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}

    const _this = this;
    // 返回一个新的promise
    return new Promise((resolve, reject) => {
      // 指定调用回调函数
      function handle(callback) {
        try {
          const result = callback(_this.data);
          if (result instanceof Promise) {  // 类型为promise,结果是这个promise的结果
            // result.then(
            //   value => resolve(value),
            //   reason => reject(reason)
            // )
            result.then(resolve, reject);
          } else {
            resolve(result);  // 非promise返回结果
          }
        } catch (error) { // 抛出异常,失败
          reject(error);
        }
      }

      if (_this.status === PENDING) {
        _this.callbacks.push({
          onResolved(value) {
            handle(onResolved)
          },
          onRejected(reason) {
            handle(onRejected)
          }
        })
      } else if (_this.status === RESOLVED) {
        setTimeout(() => {
          handle(onResolved)
        })
      } else {
        setTimeout(() => {
          handle(onRejected)
        })
      }
    })
   
  }

  /*
  Promise 原型对象.catch
  指定失败的回调函数
  返回一个新的Promise
  */
  Promise.prototype.catch = function (onRejected) {
    return this.then(undefined, onRejected)
  }

Promise.resolve()/reject()

  /*
  Promise 原型对象.resolve
  返回一个指定value的Promise
  */
  Promise.resolve = function (value) {
    // 返回一个成功/失败的promise
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(resolve, reject)
      } else {
        resolve(value)
      }
    })
  }

  /*
  Promise 原型对象.reject
  返回一个指定reason的Promise
  */
  Promise.reject = function (reason) {
    // 返回一个失败的promise
    return new Promise((resolve, reject) => {
      reject(reason);
    })
  }

Promise.all()/race()

  /*
  Promise 原型对象.all
  返回一个Promise,所有promise都成功才成功,一个失败就失败
  */
  Promise.all = function (promises) {
    const values = new Array(promises.length);  // 保存所有成功的value
    let resolveCount = 0;  // 计数器,计算成功的数量
    return new Promise((resolve, reject) => {
      // 获取每个promise的结果
      promises.forEach((p, index) => {
        p.then(
          value => {
            resolveCount++;
            values[index] = value;
            if (resolveCount === promises.length) { // 所有都完成才会resolve
              resolve(values)
            }
          }, reason => {
            reject(reason)
          }
        )
      })
    })
  }

  /*
  Promise 原型对象.race
  返回一个Promise,由第一个完成的promise决定
  */
  Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
      promises.forEach((p) => {
        p.then(
          value => {
            resolve(value)
          }, reason => {
            reject(reason)
          }
        )
      })
    })
  }

代码

代码已经上传码云


前端小白