JavaScript原生Promise实现原理与流程详解

作者:木棉

时间:2026-10-26

浏览:97

本文详细解析JavaScript中Promise的实现流程,涵盖构造函数初始化、状态管理(pending/fulfilled/rejected)、then与catch方法的链式调用逻辑,以及处理异步回调和all/race静态方法的完整实现方案。

文章目录

构造器逻辑

首先观察Promise的标准使用方式,实例化对象时的调用形态如下:

    let p1 = new Promise((resolve, reject) => {
      let random = Math.floor(Math.random() * 10);
      if (random > 4) {
        resolve('sucess')
      } else {
        reject('erro')
      }
    })

因此,在构建自定义类时,需充分考量该参数的处理机制。

分析new Promise的调用过程,传入的回调函数应当立即执行。

该回调函数接收resolve和reject两个参数,二者本身也是回调函数。

据此,构造函数内部必须包含resolve和reject这两个函数(其具体功能暂不深究)。

明确Promise具备三种状态属性:

pending:表示待定状态

fulfilled:对应resolve函数的执行结果

rejected:对应reject函数的执行结果

状态一旦变更,便不可逆转。

因此,构造函数内部需维护一个标识当前Promise状态的属性。

无论调用resolve还是reject,均会传入一个结果变量res,故需另设属性以存储该结果值。

基于上述分析,构造函数的设计如下:

function Mypromise (config) {
  this.status = 'pending';
  this.res = ''
  let resolve = (data) => {
    this.status = 'fulfilled';
    this.res = data
  }
  let reject = (data) => {
    this.status = 'rejected';
    this.res = data
  }
  config(resolve, reject)
}

then与catch方法实现

首先回顾这两个方法的常规用法:

    p1
      .then(res => {
        console.log(res);
      })
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      })

代码显示,then和catch方法均接收一个回调函数作为参数。

该回调函数的参数即为我们此前定义的this.res。

基于此逻辑,初步实现方案如下:

Mypromise.prototype.then = function (config) {
  if (this.status == 'fulfilled') {
    config(this.res)
  }
}
Mypromise.prototype.catch = function (config) {
  if (this.status == 'rejected') {
    config(this.res)
  }
}

然而,此方案无法支持链式调用,即无法连续使用then方法。

为实现链式调用模式,then方法需返回一个对象,通常该对象即为this。

直接返回this是否可行?需考察以下场景。

p1
  .then(res => {
    console.log(res);
    return new Promise((resolve, reject) => {
      resolve('1111')
    })
  })
  .then(res => {
    console.log(res);
  })
  .catch(err => {
    console.log(err);
  })

若then方法的回调返回了一个新的Promise实例,则不能直接返回当前then的this对象。

因此,需判断then的回调函数是否返回了新对象,若无,则返回当前then的this对象。

Mypromise.prototype.then = function (config) {
  if (this.status == 'fulfilled') {
    var res = config(this.res)
  }
  return res || this;
}
Mypromise.prototype.catch = function (config) {
  if (this.status == 'rejected') {
    var res = config(this.res)
  }
  return res || this;
}

异步处理机制

上述代码看似无懈可击,但若采用如下写法:

    let p2 = new Mypromise((resolve, reject) => {
      setTimeout(() => {
        reject('p2 resolve')
      }, 1000);
    })

问题便暴露无遗。原因在于执行p2.then时,定时器尚未结束,p2状态仍为pending,导致后续逻辑无法执行。

此处采用一种经典解决方案,在之前的axios和路由实现中亦常见此模式。

在then方法中,若当前状态为pending(关键点),则需将当前回调函数保存下来(可能存在多个then,故采用数组存储)。

保存的回调何时执行?显然是在定时器结束后。而定时器何时结束?即Promise状态变更时。因此,需在resolve和reject方法中调用这些保存的方法。

据此,需修改构造函数:

function Mypromise (config) {
  this.status = 'pending';
  this.res = '';
  this.saveResolve = [];
  this.saveReject = [];
  let resolve = (data) => {
    if (this.status == 'pending') {
      this.status = 'fulfilled';
      this.res = data
      this.saveResolve.forEach(val => {
        val(this.res)
      })
    }
  }
  let reject = (data) => {
    if (this.status == 'pending') {
      this.status = 'rejected';
      this.res = data
      this.saveReject.forEach(val => {
        val(this.res)
      })
    }
  }
  config(resolve, reject);
}

随后修改then和catch方法:

Mypromise.prototype.then = function (config) {
  if (this.status == 'pending') {
    this.saveResolve.push(config);
  }
  if (this.status == 'fulfilled') {
    var res = config(this.res)
  }
  return res || this;
}
Mypromise.prototype.catch = function (config) {
  if (this.status == 'pending') {
    this.saveReject.push(config)
  }
  if (this.status == 'rejected') {
    var res = config(this.res)
  }
  return res || this;
}

至此,异步处理问题得以解决。

all与race方法实现

沿用惯例,编写前回顾其用法:

    Mypromise.all([p2, p3, p4])
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      })
    Mypromise.race([p2, p3, p4])
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      })

二者均以数组为参数,此处暂不考虑其他情况,假设数组内全为Promise对象。

两者的区别在于:

all:当所有Promise均执行完毕且状态均为fulfilled时,all返回的Promise为fulfilled,否则为rejected。

race:首个产生结果的Promise即为race返回的Promise的结果。

接下来思考all方法的实现,获取数组参数后,必须遍历处理。

对每个元素调用then和catch方法。

then方法需维护一个结果数组,用于保存每个Promise的结果值。

可利用计数器统计then方法的调用次数,若计数器值等于数组长度,则证明所有Promise均为fulfilled,可返回结果数组。

catch方法一旦被调用,直接返回结果,无需额外处理。

最后,务必返回新的Promise对象。

Mypromise.all = function (arr) {
  let result = [];
  let count = 0;
  let promise = new Mypromise((resolve, reject) => {
    for (var i = 0; i < arr.length; i++) {
      arr[i]
        .then(res => {
          result.push(res);
          count++;
          if (count == arr.length) resolve(result);
        })
        .catch(err => {
          reject(err)
        })
    }
  })
  return promise
}

race方法的实现更为简洁,无论哪个Promise的then或catch方法触发,直接返回结果:

Mypromise.race = function (arr) {
  let promise = new Mypromise((resolve, reject) => {
    for (var i = 0; i < arr.length; i++) {
      arr[i]
        .then(res => {
          resolve(res);
        })
        .catch(err => {
          reject(err)
        })
    }
  })
  return promise
}

关于JavaScript实现Promise流程的详解至此结束,更多JS Promise相关内容请参阅本站往期文章或继续浏览下文,感谢支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章