学习笔记之Promise

130 阅读6分钟

一 基本理论

简单介绍

我们可以任何函数或者是异步函数改为Promise,尤其是异步函数,改为Promise之后可以进行链式调用,增强代码可读性.

原理介绍

  1. Promise有三种状态,进行中,已完成,已拒绝,进行中状态可以更改为已完成已拒绝,经过更改后无法无法继续更改.
  2. Promise的构造函数,需要传入一个函数,这个函数接受两个函数作为参数.如果执行第一个参数,则当前Promise状态会变为已完成;如果执行第二参数,则当前Promise状态会变为已拒绝;
  3. 对于已完成的Promise,我们可以通过实例方法then第一个参数进行回调;
  4. 对于已拒绝的Promise,我们可以:(1)通过后续的实例方法catch (2)或是实例方法then的第二个参数 (3)或是try..catch进行捕获.

二 深入挖掘

规范介绍

定义

任何符合Promise规范的对象或函数都可以称为Promise,(Promise A plus 规范地址)

术语

  • Promise: 是拥有then方法的对象或函数,其行为符合规范
  • 具有then方法(thanable);
  • 其值为JavaScript的任何合法值;
  • 异常(exception):是使用throw语句抛出的一个值;
  • 原因(reason):表示Promise的拒绝原因;

概念

  • Promies的状态

Promise的状态必须为等待态(Pending),已完成(Fulfilled),已拒绝(Rejected)三种状态之一. 等待态状态可以更改为已完成已拒绝,经过更改后无法无法继续更改. 已完成状态必须包含一个值 已拒绝状态必须包含一个原因

  • Promise必须提供一个then方法,以访问当前值和原因

then方法:接受两个可选参数onFulFilled,onRejected,而且他们都是函数,如果不是函数,则忽略他们; 如果onFulfilled是一个函数,当Promise已完成之后,会以Promise的值作为第一个参数去调用它; 如果onRejected是一个函数,当Promise已拒绝之后,会以Promise的原因作为第一个参数去调用它;

  • 仅包含平台代码的执行上下文堆栈之前,不得调用onFulFilled,onRejected;

  • onFulFilled,onRejected必须作为普通函数调用;(非实例化调用,这样非严格模式下内部this会指向window)

  • 在同一个Promise中then可以被多次调用:

当promise为已完成的时候,所有的onFulfilled需按照其注册的顺序依次调用. 当promsie为已拒绝的时候,所有的onRejected需按照其注册的顺序依次调用.

  • then方法必须返回一个Promise对象,在链式调用中:

如果onFulfilled或者onRejected返回一个值,则返回的下一个Promise对象会进入已完成状态; 如果onFulfilled或者onRejected抛出一个异常,则返回的下一个Promise对象会进入已拒绝状态; 如果onFulfilled是非函数,且当前Promise状态为已完成,则返回的下一个Promise状态必须为已完成且以当前Promise的值作为第一个回调参数; 如果onRejected是非函数,且当前Promise状态为已拒绝,则返回的下一个Promise状态必须为已拒绝且以当前Promise的拒绝原因作为第一个回调参数;

静态方法介绍

  • Promise.resolve:返回一个Promise实例,并将它的状态设置为已完成,并将它传入的参数作为Promise实例的值;

  • Promise.reject:返回一个Promise实例,并将它的状态设置为已拒绝,并将它传入的参数作为Promise实例的拒绝原因;

  • Promise.all:接受一个数组,其中包含多个Promise实例,然后返回一个Promise实例;

    • 当传入的所有实例都为已完成状态的时候,返回的Promise为已完成状态;
    • 当传入的实例中都任何一个为已拒绝状态的时候,染回的Promise为已拒绝状态;
  • Promise.race:接受一个数组,其中包含多个Promise实例,然后返回一个Promise实例;

    • 传入的所有Promise实例互为竞争关系
    • 返回第一个改变状态的Promise实例的状态作为返回的Promise实例的状态;

三 相关拓展

generator和async/await

ES6+当中,我们可以使用generator和async/await来操作Promise,比Promise来说他们在语法层面的让调用关系显得更加串行

四 代码实践

1. 使用Promise封装异步操作案例

  • 类比:传统回调
function ajax(url,success,fail) {
  var client = new XMLHttpRequest();
  client.open("GET",url);
  client.onreadystatechange = function() {
    if(this.readyState !== 4) {
      return
    }
    if(this.status === 200) {
      success(this.response);
    }
    else {
      fail(new Error(this.statusText));
    }
  }
  client.send();
}

ajax(
  '/ajax.json',
  function(){
    console.log("success!")
  },
  function(){
    console.log("fail!")
  }
)
  • Promise封装
// Promise封装
function ajaxAsync(url) {
  return new Promise(function(resolve,reject) {
    var client = new XMLHttpRequest();
    client.open();
    client.onreadystatechange = function() {
      if(this.readyState !== 4) {
        return
      }
      if(this.state === 200) {
        resolve(this.response)
      }
      else {
        reject(new Error(this.statusText))
      }
    }
    client.send();
  })
}

ajaxAsync('/ajax.json')
.catch(function(){
  console.log("fail!")
})
.then(function() {
  console.log("success!")
})

2. 链式调用两个Promise

/** @desc define function by Promise */
function promiseCreator() {
  return new Promise(function(resolve){
    setTimeout(resolve, 1000,'1s');
  })
}

function promiseCreator2() {
  return new Promise(function(resolve){
    setTimeout(resolve, 1000,'2s');
  })
}

var promiseCreatorList = [
  promiseCreator,
  promiseCreator2,
];

/** @desc run by async/await  */
async function runPromisesByForOf(list) {
  for (const promise of list) {
    await promise()
  }
}

runPromisesByForOf(promiseCreatorList)

/** @desc run by reduce */
function runPromisesByReduce(list) {
  list.reduce(
    (prev, cur) => prev.then(cur),
    Promise.resolve()
  )
}
runPromisesByReduce(promiseCreatorList)

3. 简单封装Promise

功能点: 1.

class Promise {
  constructor(executor) {
    if (typeof executor !== "function") {
      throw new TypeError("Executor of Promise must be a function.")
    }
    this.init()
    try {
      executor(this.resolve, this.reject)
    }
    catch (e) {
      this.reject(e)
    }
  }
  init() {
    this.value = null;
    this.reason = null;
    this.status = Promise.PENDING;
    this.onFulfilledCbs = [];
    this.onRejectedCbs = [];
    this.resolve = this.resolve.bind(this);
    this.reject = this.reject.bind(this);
  }
  resolve(val) {
    if (this.status === Promise.PENDING) {
      this.status = Promise.FULFILLED;
      this.value = val;
      this.onFulfilledCbs.forEach(fn => fn(this.value))
    }
  }
  reject(err) {
    if (this.status === Promise.PENDING) {
      this.status = Promise.REJECTED;
      this.reason = err;
      this.onRejectedCbs.forEach(fn => fn(this.reason))
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled !== 'function' ? val => val : onFulfilled;
    onRejected = typeof onRejected !== 'function' ? reason => { throw reason } : onRejected;
    /** 递归构造一个new Promise */
    let promise2 = new Promise((resolve, reject) => {
      /** 分三种情况 */
      if (this.status === Promise.FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            /** 参数: 调用后返回一个thenable*/
            Promise.resolvePromise(promise2, x, resolve, reject)
          }
          catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === Promise.REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            /** 参数: 调用后返回一个thenable */
            Promise.resolvePromise(promise2, x, resolve, reject)
          }
          catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === Promise.PENDING) {
        /** 如果是PENDING,就就会添加一个包装过后的完成和拒绝函数,结构跟现实调用的一致 */
        this.onFulfilledCbs.push(value => {
          setTimeout(() => {
            try {
              let x = onFulfilled(value);
              Promise.resolvePromise(promise2, x, resolve, reject)
            }
            catch (e) {
              reject(e)
            }
          })
        })
        this.onRejectedCbs.push(reason => {
          setTimeout(() => {
            try {
              let x = onRejected(reason)
              Promise.resolvePromise(promise2, x, resolve, reject)
            }
            catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return promise2
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  static all(list) {
    return new CustomPromise((resolve, reject) => {
      let count = 0;
      const values = [];
      for (const [i, customPromiseInstance] of list.entries()) {
        customPromiseInstance
          .then(
            res => {
              values[i] = res;
              count++;
              if (count === list.length) resolve(values);
            },
            err => {
              reject(err);
            }
          )
      }
    });
  }

  static resolve(val) {
    return new CustomPromise(resolve => resolve(val));
  }

}

Promise.PENDING = "pending";
Promise.FULFILLED = "fulfilled";
Promise.REJECTED = "rejected";

/** Promise.resolve */
Promise.resolvePromise = function (promise2, x, resolve, reject) {
  /** 防止循环引用 */
  if (promise2 == x) {
    return reject(new TypeError("Chaining cycle detected for promise"))
  }
  let called;
  /** 有效回调函数 */
  if (x !== null && typeof x === "object" || typeof x === "function") {
    try {
      // 有then
      let then = x.then;
      if (typeof then === "function") {
        // 为保证上下文环境绑定自己作为执行上下文.
        than.call(
          x,
          value => {
            // 如果已经调用了就不在调用了
            if (called) return;
            called = true;
            // 继续递归调用
            Promise.resolvePromise(promise2, value, resolve, reject)
          },
          err => {
            if (called) return;
            called = true;
            // 拒绝
            reject(err)
          }
        )
      } else {
        // x 为基本类型
        resolve(x)
      }
    } catch (e) {
      reject(e)
    }
  }
}