Promise从0到自定义

Promise从0到自定义

1. Promise是什么?

Promise是一个构造函数,用于表示一个异步操作的最终完成 (或失败), 及其结果值.

2. Promise用来解决我们开发中什么问题?

Promise用来解决我们日常开发中产生的回调嵌套(回调地狱)问题.

3. Promise实例对象内部的三种状态

* pending:初始状态,既不是成功,也不是失败状态
* resolved/fulfilled: 成功状态
* rejected:失败状态

4. Promise语法研究

1. Promise构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数
2. Promise构造函数执行时立即调用executor函数
3. executor函数接受两个函数(resolve和reject)作为其参数
4. resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)
5. resolve/reject函数分别都接受一个参数,供之后调用then/catch函数传递的参数,默认是undefined
6. 当异步任务顺利完成且返回结果值时,会调用 resolve 函数
7. 当异步任务失败且返回失败原因时,会调用reject 函数
8. 一个promise内部状态只能被修改一次
9. executor函数中抛出一个错误,那么该promise 状态为rejected
// 按照以上对Promise语法的研究,我们可以写出以下代码
function MyPromise (executor) {
  this._status = 'pending' // 初始化状态
  this._value = undefined

  // 成功的回调
  const resolve = value => {
    // 一旦状态不是pending,说明肯定调用了resolve/reject函数,那么就不能让它继续执行,因为一个		   promise内部状态只能改变一次
    if (this._status !== 'pending') return
    this._status = 'resolved' // 将实例对象上的状态改为成功
    this._value = value // 拿到传进来的参数并赋值给这个promise实例上的value
  }

  // 失败的回调
  const reject = reason => {
    if (this._status !== 'pending') return
    this._status = 'rejected'
    this._value = reason
  }

  // 捕获处理器函数内部异常
  try {
    executor(resolve, reject)
  } catch (error) {
    reject(error)
  }
}
const promise = new MyPromise((resolve, reject) => {
  resolve(1111)
  reject(222)
})
console.log(promise)
/**
浏览器打印如下:
MyPromise {_status: "resolved", _value: 1111}
    _status: "resolved"
    _value: 1111
    __proto__:
    constructor: ƒ MyPromise(executor)
    __proto__: Object
**/

5. Promise原型上的方法

1. then(onResolved,onRejected) ==> 添加成功(onResolved)和失败(onRejected)回调到当前 promise,    返回一个新的 promise, 将以回调的返回值来resolve
2. catch(onRejected) ==> 添加一个失败(rejection) 回调到当前 promise, 返回一个新的promise。当这个    回调函数被调用,新 promise 将以它的返回值来resolve,否则如果当前promise 进入fulfilled状态,则以当    前promise的完成结果作为新promise的完成结果.
3. finally() ==> 添加一个事件处理回调于当前promise对象,并且在原promise对象解析完毕后,返回一个新的      promise对象。回调会在当前promise运行完毕后被调用,无论当前promise的状态是完成(fulfilled)还是失败    (rejected)
// 这次我们就研究一下then方法的实现

6. then语法研究

1. then() 方法返回一个 Promise。它最多需要有两个参数:Promise 的成功和失败情况的回调函数
2. then方法内部接受2个回调函数,onResolved/onRejected 对应 `成功和失败的回调`
3. 当上一个promise状态变成`resolved(成功)`,调用`onResolved回调`
4. 当上一个promise状态变成`rejected(失败)`,调用`onRejected回调`
5. onResolved/onRejected回调中都接受一个参数,参数内容为上一个promise内部传递的值

7. then方法返回的Promise状态研究

1. 回调函数内部返回了一个值,那么就返回一个成功状态的promise,并且将返回的值作为接受状态的回调函数的参数值
2. 没有返回任何值,那么 then 返回的 Promise 将会成为成功状态,该接受状态的回调函数的参数值为 undefined
3. 抛出一个错误,那么 then 返回的 Promise 将会成为失败状态,并且将抛出的错误作为失败状态回调函数的参数值
4. 返回一个已经是成功状态的 Promise,那么 then 返回的 Promise 也会成为成功状态,并且将那个 Promise      的成功状态的回调函数的参数值作为该被返回的Promise的成功状态回调函数的参数值。
5. 返回一个已经是失败状态的 Promise,那么 then 返回的 Promise 也会成为失败状态,并且将那个 Promise      的失败状态的回调函数的参数值作为该被返回的Promise的失败状态回调函数的参数值。
6. 返回一个未定状态(pending)的 Promise,那么 then 返回 Promise 的状态也是未定的,并且它的终态与那个    Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是    相同的。
先简单实现 不同状态调用不同回调:
	MyPromise.prototype.then = function (onResolved, onRejected) {
          if (this._status === 'resolved') {
            // 调用成功的回调
            onResolved(this._value)
          } else if (this._status === 'rejected') {
            // 调用失败的回调
            onRejected(this._value)
          } else {
            // 现在不处理
          }
	}

8. then方法的链式调用

链式调用:返回值还是一个promise,至于是什么状态的promise看上面我们说的
## 怎么确定一个函数的返回值还是一个promise呢?
	return new Promise((resolve,reject) =>{}) 作为其返回值
## 返回的promise是什么状态的?
	看上面总结的
## 我们来实现
	MyPromise.prototype.then = function (onResolved, onRejected) {
          return new Promise((resolve, reject) => {
            if (this._status === 'resolved') {
              // 调用成功的回调,并且拿到该成功回调函数内部的返回值
              const res = onResolved(this._value)
              // 判断该返回值是不是一个promise实例,如果是的话,我们继续调用then方法判断内部状态即可
              // 如果不是一个promise实例,那么默认就是成功,并且将回调函数内部返回值作为其参数传入
              // 如果内部抛出异常,那么返回值是一个失败的promise
              try {
                res instanceof MyPromise ? res.then(resolve, reject) : resolve(res)
              } catch (error) {
                // 捕获错误,并且返回一个失败的promise
                reject(error)
              }
            } else if (this._status === 'rejected') {
              // 调用失败的回调
              const res = onRejected(this._value)
              try {
                res instanceof MyPromise ? res.then(resolve, reject) : resolve(res)
              } catch (error) {
                reject(error)
              }
            } else {
              // 现在不处理
            }
          })
	}

## 测试:
	const promise = new MyPromise((resolve, reject) => {
      reject(1)
    })
    promise.then(value => {
      console.log(value)
    }, reason => {
      console.log(reason)
    })
      .then(value => {
        console.log(2)
      })
      .then(value => {
        console.log(3)
      })
      .then(value => {
        console.log(4)
      })
测试结果:1 2 3 4
// 这样我们就实现了promise的链式调用

9. pending状态内部如何处理?

`上面我们实现了promise的链式调用,那么我们来思考一个问题: 如果内部是异步执行,那么结果会是怎样的呢?`
代码如下:
	const promise = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve(1111)
      }, 1000)
    })
    promise.then(value => {
      console.log(value)
    }, reason => {
      console.log(reason)
    })
      .then(value => {
        console.log(2222)
      })
      .then(value => {
        console.log(3333)
      })
      .then(value => {
        console.log(4444)
      })
测试结果: `没有任何输出`
那咱们来捋一下代码逻辑:
	`当我们去new Promise的时候,我们会去调用executor执行器函数,然后定时器1s后执行,但是我们代码并不会等待定时器到点了在去执行下面代码,也就是说定时器在等待中的时候我们的then方法就已经调用了,但是此时我们的promise内部状态还是pending状态,所以并不会触发任何一个回调,然后代码执行完之后,定时器到点,调用resolve函数,resolve函数内部仅仅改变了状态和value值,并没有去调用then方法内部的任何一个回调,所以结果没有任何输出,也就是说,在promise处于pending状态的时候,我们并没有做任何事情`
# 思考: pending状态的时候应该做什么事情?
		`我们可以在pending状态的时候用一个容器存储着成功和失败的回调,然后在resolve/reject函数内部去调用这个容器中存储的成功或者失败回调`
那么咱们来实现一下:
	MyPromise.prototype.then = function (onResolved, onRejected) {
      return new Promise((resolve, reject) => {
        if (this._status === 'resolved') {
          // 调用成功的回调,并且拿到该成功回调函数内部的返回值
          const res = onResolved(this._value)
          // 判断该返回值是不是一个promise实例,如果是的话,我们继续调用then方法判断内部状态即可
          // 如果不是一个promise实例,那么默认就是成功,并且将回调函数内部返回值作为其参数传入
          // 如果内部抛出异常,那么返回值是一个失败的promise
          try {
            res instanceof MyPromise ? res.then(resolve, reject) : resolve(res)
          } catch (error) {
            // 捕获错误,并且返回一个失败的promise
            reject(error)
          }
        } else if (this._status === 'rejected') {
          // 调用失败的回调
          const res = onRejected(this._value)
          try {
            res instanceof MyPromise ? res.then(resolve, reject) : resolve(res)
          } catch (error) {
            reject(error)
          }
        } else {
          // 当处于pending状态的时候,我们给容器中push一个成功和失败的回调
          this._callbacks.push({
            // 使用箭头函数解决内部this指向问题
            onResolved: () => {
              const res = onResolved(this._value)
              try {
                res instanceof MyPromise ? res.then(resolve, reject) : resolve(res)
              } catch (error) {
                reject(error)
              }
            },
            onRejected: () => {
              const res = onRejected(this._value)
              try {
                res instanceof MyPromise ? res.then(resolve, reject) : resolve(res)
              } catch (error) {
                reject(error)
              }
            }
          })
        }
      })
}
Promise构造函数中代码如下:
	function MyPromise (executor) {
      this._status = 'pending' // 初始化状态
      this._value = undefined
      this._callbacks = [] // 存储成功或者失败的回调

      // 成功的回调
      const resolve = value => {
        // 一旦状态不是pending,说明肯定调用了resolve/reject函数,那么就不能让它继续执行,因为一个			promise内部状态只能改变一次
        if (this._status !== 'pending') return
        this._status = 'resolved' // 将实例对象上的状态改为成功
        this._value = value // 拿到传进来的参数并赋值给这个promise实例上的value
        // 先判断容器中是否存储了回调,如果存储了那就遍历调用所有的成功回调
        this._callbacks.length && this._callbacks.forEach(cb => cb.onResolved(value))
      }

      // 失败的回调
      const reject = reason => {
        if (this._status !== 'pending') return
        this._status = 'rejected' // 将实例对象上的状态改为失败
        this._value = reason
        this._callbacks.length && this._callbacks.forEach(cb => cb.onRejected(reason))
      }

      // 捕获处理器函数内部异常
      try {
        executor(resolve, reject)
      } catch (error) {
        reject(error)
      }
}
// 那么我们的then方法就实现了
// 提取一下then方法内部的公共代码
MyPromise.prototype.then = function (onResolved, onRejected) {
  return new Promise((resolve, reject) => {
    switch (this._status) {
      case 'resolved':
        PromiseThenPublicCode(onResolved, this._value, resolve, reject)
        break;
      case 'rejected':
        PromiseThenPublicCode(onRejected, this._value, resolve, reject)
        break;
      default:
        // 当处于pending状态的时候,我们给容器中push一个成功和失败的回调
        this._callbacks.push({
          // 使用箭头函数解决内部this指向问题
          onResolved: () => {
            PromiseThenPublicCode(onResolved, this._value, resolve, reject)
          },
          onRejected: () => {
            PromiseThenPublicCode(onRejected, this._value, resolve, reject)
          }
        })
        break;
    }
  })
}
// 提取then方法内部的公共代码
function PromiseThenPublicCode (onFn, value, resolve, reject) {
  // 调用成功的回调,并且拿到该成功回调函数内部的返回值
  const res = onFn(value)
  // 判断该返回值是不是一个promise实例,如果是的话,我们继续调用then方法判断内部状态即可
  // 如果不是一个promise实例,那么默认就是成功,并且将回调函数内部返回值作为其参数传入
  // 如果内部抛出异常,那么返回值是一个失败的promise
  try {
    res instanceof MyPromise ? res.then(resolve, reject) : resolve(res)
  } catch (error) {
    // 捕获错误,并且返回一个失败的promise
    reject(error)
  }
}
// 那么我们内部就可以直接调用这个函数
MyPromise.prototype.then = function (onResolved, onRejected) {
  return new Promise((resolve, reject) => {
    switch (this._status) {
      case 'resolved':
        PromiseThenPublicCode(onResolved, this._value, resolve, reject)
        break;
      case 'rejected':
        PromiseThenPublicCode(onRejected, this._value, resolve, reject)
        break;
      default:
        // 当处于pending状态的时候,我们给容器中push一个成功和失败的回调
        this._callbacks.push({
          // 使用箭头函数解决内部this指向问题
          onResolved: () => {
            PromiseThenPublicCode(onResolved, this._value, resolve, reject)
          },
          onRejected: () => {
            PromiseThenPublicCode(onRejected, this._value, resolve, reject)
          }
        })
        break;
    }
  })
}
// 最后我们的代码还有一个小问题
	console.log(0)
    const promise = new MyPromise((resolve, reject) => {
      resolve(1)
    })
    promise
      .then(value => {
        console.log(value)
      })
      .then(value => {
        console.log(2)
      })
      .then(value => {
        console.log(3)
      })
      .then(value => {
        console.log(4)
      })

    console.log(5)
// 正常打印顺序应该是 0 5 1 2 3 4
// 但是我们的打印顺序是 0 1 5 2 3 4
// 是因为我们then方法实现内部并没有异步去执行,我们只需要包一层定时器模拟一下异步即可
function PromiseThenPublicCode (onFn, value, resolve, reject) {
  setTimeout(() => {
    // 调用成功的回调,并且拿到该成功回调函数内部的返回值
    const res = onFn(value)
    // 判断该返回值是不是一个promise实例,如果是的话,我们继续调用then方法判断内部状态即可
    // 如果不是一个promise实例,那么默认就是成功,并且将回调函数内部返回值作为其参数传入
    // 如果内部抛出异常,那么返回值是一个失败的promise
    try {
      res instanceof MyPromise ? res.then(resolve, reject) : resolve(res)
    } catch (error) {
      // 捕获错误,并且返回一个失败的promise
      reject(error)
    }
  })
}
// 那么咱们的then方法就实现了

猜你喜欢

转载自blog.csdn.net/shkstart/article/details/107738394