Promise详解

84 阅读3分钟

使用

基本使用

(new Promise(executor))
  .then(onFulfilled,onRejected)

// 也可以不指定onRejected,那么使用默认onRejected
(new Promise(executor))
  .then(onFulfilled,onRejected)

// 或者不指定onFulfilled,同样的会使用默认onFulfilled
(new Promise(executor))
  .catch(onRejected)

默认onFulfilled和onRejected函数大概是这样:

const defaultOnFulfilled = v => v
const defaultOnRejected = error => throw error

then和catch都是返回一个新的promise.

Promise的链式调用

promise.then(),promise.catch() 和 promise.finally()都会新生成的 promise 对象。

const myPromise =
  (new Promise(executor))
  .then(onFulfilledA,onRejectedA)
  .then(onFulfilledB,onRejectedB)
  .then(onFulfilledC,onRejectedC);

// 或者,这样可能会更好...

const myPromise =
  (new Promise(executor))
  .then(onFulfilledA)
  .then(onFulfilledB)
  .then(onFulfilledC)
  .catch(onRejectedAny);

这些函数的返回决定着链式调用中下一个promise的状态是什么,throw导致rejected,其他导致resolved:

onFulfilled(value)       { /*...*/; return nextValue;  }
onRejection(reason)  { /*...*/; throw  nextReason; }
onRejection(reason)  { /*...*/; return nextValue;  }

对于代码:

const promiseA = new Promise(executor);
const promiseB = promiseA.then(onFulfilledB, onRejectedB);
const promiseC = promiseB.then(onFulfilledC, onRejectedC); 
const promiseD = promiseB.then(onFulfilledD, onRejecteD); 

这些promise们关系如下图所示:

20220424161921

每个promise依赖它的上一个promise, 换个角度,我们也可以认为, 上个promise的输入是下一个promise的输出。

被返回的 nextValue 可能是另一个promise对象,这种情况下这个promise会被动态地插入链式调用。

链式调用中的 promise 们是嵌套起来的,像是一个栈:

(promise D, (promise C, (promise B, (promise A) ) ) )

当存在一个 nextValue 是 promise 时,就会出现一种动态的替换效果。return 会导致一个 promise 被弹出,但这个 nextValue promise 则会被推入被弹出 promise 原来的位置。对于上面所示的嵌套场景,假设与 "promise B" 相关的 .then() 返回了一个值为 "promise X" 的 nextValue 。那么嵌套的结果看起来就会是这样:

(promise D, (promise C, (promise X) ) )

手写 Promise

简易版 Promise

const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'



class Promise {
  constructor(fn) {
    this.state = PENDING
    this.value = null
    this.resolvedCallbacks = []
    this.rejectedCallbacks = []

    try {
      fn(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }

  resolve(value) {
    if (this.state === PENDING) {
      this.state = RESOLVED
      this.value = value
      this.resolvedCallbacks.map(cb => cb(this.value))
    }
  }

  reject(value) {
    if (this.state === PENDING) {
      this.state = REJECTED
      this.value = value
      this.rejectedCallbacks.map(cb => cb(this.value))
    }
  }

  then(onFulfilled, onRejected) {
    // 默认的onFulfilled和onRejected
    const defaultOnFulfilled = v => v
    const defaultOnRejected = error => throw error

    onFulfilled = onFulfilled ||  defaultOnFulfilled
    onRejected = onRejected || defaultOnRejected

    // 如果状态为PENDING, 则加入callbacks
    if (this.state === PENDING) {
      this.resolvedCallbacks.push(onFulfilled)
      this.rejectedCallbacks.push(onRejected)
    }

    // 否则直接执行
    if (this.state === RESOLVED) {
      onFulfilled(this.value)
    }
    if (this.state === REJECTED) {
      onRejected(this.value)
    }
  }
}

then 的链式调用&值穿透特性

我们改写then, 让它返回一个新的promise:

then(onFulfilled, onRejected) {
  ...
  return new Promise((resolve, reject) => {
    // 注意: 为了简单,并没有处理resutl为promise的情况
    if (this.state === PENDING) {
      this.resolvedCallbacks.push(() => {
        try {
          const result = onFulfilled(this.value)
          resolve(result)
        } catch (error) {
          reject(error)
        }
      })

      this.rejectedCallbacks.push(() => {
        try {
          const result = onRejected(this.value)
          reject(result)
        } catch (error) {
          reject(error)
        }
      })
    }

    if (this.state === RESOLVED) {
      try {
        const result = onFulfilled(this.value)
        resolve(result)
      } catch (error) {
        reject(error)
      }
    }
    if (this.state === REJECTED) {
      try {
        const result = onRejected(this.value)
        reject(result)
      } catch (error) {
        reject(error)
      }
    }
  })
}

模拟微任务

onFulfilled和onRejected是异步执行的, 因为我们不能直接添加微任务, 我们只好用setTimeout(fn, 0)来模拟:

resolve(value) {
  setTimeout(() => {
    if (this.state === PENDING) {
      this.state = RESOLVED
      this.value = value
      this.resolvedCallbacks.map(cb => cb(this.value))
    }
  }, 0)
}
reject(value) {
  setTimeout(() => {
    if (this.state === PENDING) {
      this.state = REJECTED
      this.value = value
      this.rejectedCallbacks.map(cb => cb(this.value))
    }
  }, 0)
}
then() {
  if (this.state === PENDING) { ... }
  if (this.state === RESOLVED) {
    setTimeout(() => {
      try {
        const result = onFulfilled(this.value)
        resolve(result)
      } catch (error) {
        reject(error)
      }
    }, 0)
      
    }
  if (this.state === REJECTED) {
    setTimeout(() => {
      try {
        const result = onRejected(this.value)
        reject(result)
      } catch (error) {
        reject(error)
      }
    }, 0)
  }
}

添加微任务时机:

  1. 当状态由 PENDING 变为 RESOLVED 或 REJECTED 时(调用resolve()或reject()), 将 this.resolvedCallbacks 或 this.rejectedCallbacks 中的函数添加到微任务队列。

  2. 或者在状态已经为 RESOLVED 或 REJECTED 时,调用then方法,会直接将 onFulfilled 或 onRejected 函数加入到微任务队列。

我们看个例子:

const promise = Promise.resolve(1)
promise.then(val => console.log(val)) // 状态已经为RESOLVED, 执行完这句,函数val => console.log(val) 加入微任务队列,但是要等到本次宏任务执行完成之后再执行
console.log(2)

// 结果: 2, 1

思考题

console.log('1');
setTimeout(() => {
    console.log("2")
    Promise.resolve().then(() => {
        console.log("3")
    })
}, 0);
new Promise((resolve, reject) => {
    console.log("4")
    setTimeout(() => {
        console.log("5")
        resolve("6");
    }, 0);
}).then((res) => {
    console.log("7");
    setTimeout(() => {
        console.log(res)
    }, 0);
}).then(() => {
    console.log("8");
}).then(() => {
    console.log("9");
});
  • 第一轮:宏任务1, 4; 微任务:空
  • 第二轮:宏任务2; 微任务:3
  • 第三轮:宏任务5, 微任务:resolve了,把对应微任务加入~~7、8、9
  • 第四轮:6 结果依次输出:1 4 2 3 5 7 8 9 6

参考链接