Skip to content

JavaScript之Promise主要功能模拟实现 #10

@chenyong9528

Description

@chenyong9528

Promise 的含义

阮一峰老师的《ECMAScript 6 入门》对它的定义:

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

简单的看看Promise的使用:

const pm = new Promise((resolve) => {
  setTimeout(() => {
    resolve(10)
  }, 1000)
})

pm.then((res) => {
  console.log(res) // 1s后打印10
})

如定义所述,Promise中保存着将来会完成的一件事,完成时就会执行then中的回调函数。

我们要实现的是Promise的简单版myPromise,它能够让下面的代码打印正确的结果:

const mp = new MyPromise((resolve) => {
  setTimeout(function() {
    resolve(10)
  }, 1000)
})

mp
.then((res) => {
  console.log(res)

  return new MyPromise((resolve) => {
    setTimeout(() => {
      resolve(res + 10)
    }, 1000)
  })
})
.then((res) => {
  console.log(res)
})

// 1s后打印10
// 2s后打印20

实现简单版myPromise

myPromise主要包括两个功能:

  1. 异步事件有结果了(resolve()),立即执行then中的回调函数
  2. 可以链式调用

第一版:

function MyPromise(fn) {
  this.respFn = null

  const resolve = (val) => {
    this.respFn(val)
  }

  fn(resolve)
}

MyPromise.prototype.then = function(f) {
  this.respFn = f
}

const mp = new MyPromise((resolve) => {
  setTimeout(function() {
    resolve(10)
  }, 1000)
})

mp.then((res) => {
  console.log(res) // 1s后打印10
})

这个比较好理解,实例中的respFn属性用来保存then中的回调函数f,当resolve()时会立即去执行该函数。这样就实现了第一个功能。

关于第二个功能就有点绕了,首先then返回一个MyPromise是明确的,但是f不再直接赋值给respFn

第二版:

function MyPromise(fn) {
  this.respFn = null

  const resolve = (val) => {
    this.respFn(val)
  }

  fn(resolve)
}

MyPromise.prototype.then = function(f) {

  return new MyPromise((resolve) => {
    this.respFn = (val) => {

      const fv = f(val)

      if (fv instanceof MyPromise) {
        fv.then(resolve)
      } else {
        resolve(fv)
      }
    }
  })
}

const mp = new MyPromise((resolve) => {
  setTimeout(function() {
    resolve(10)
  }, 1000)
})

mp
.then((res) => {
  console.log(res)

  return new MyPromise((resolve) => {
    setTimeout(() => {
      resolve(res + 10)
    }, 1000)
  })
})
.then((res) => {
  console.log(res)
})

// 1s后打印10
// 2s后打印20

看看以上代码,respFn被赋值为了一个新函数,而then中的回调函数f则是在新函数中去调用。想一想,第二个then的回调函数的执行时机是由第一个then方法返回的MyPromise决定的,而MyPromise什么时候resolve()又是函数f的返回值决定的,所以,我们需要一个新函数作为中间者,在新函数中拿到f的返回值再决定resolve()的时机。从if else这段代码可以看出来,当f的返回值不是MyPromise时会直接resolve(),否则会等f返回的MyPromise解决了才会resolve()

最后在处理一下MyPromise中不做异步而是直接resolve()和respFn不是函数的情况

最终版:

function MyPromise(fn) {
  this.respFn = null

  const resolve = (val) => {
    queueMicrotask(() => {
      if (this.respFn instanceof Function) {
        this.respFn(val)
      }
    })
  }

  fn(resolve)
}

MyPromise.prototype.then = function(f) {

  return new MyPromise((resolve) => {
    this.respFn = (val) => {

      const fv = f(val)

      if (fv instanceof MyPromise) {
        fv.then(resolve)
      } else {
        resolve(fv)
      }
    }
  })
}

你品,你细细的品

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions