ES6 Promise 核心用法与链式调用详解

作者:代码拾荒时间:2026-10-23 15:54:02浏览:86

本文详细介绍 ES6 中 Promise 的创建、状态管理及链式调用机制。通过对比传统回调函数,阐述如何利用 then、catch 和 finally 解决回调地狱问题,并解析 all 与 race 等静态方法的并行处理逻辑,帮助开发者掌握现代 JavaScript 异步编程的核心技巧。

Promise 概述与回调地狱问题

本文通过具体实例解析 ES6 中 Promise 的核心用法,旨在为读者提供实用的参考与学习素材。

JavaScript 采用单线程执行模型,对于耗时较长的 I/O 操作,通常依赖异步回调函数来处理。

然而,这种模式存在明显缺陷:当后续操作依赖前一个操作的结果时,代码必须嵌套在前一个操作的回调函数内部。层层嵌套会导致代码结构混乱,形成所谓的“回调地狱”。


$.get('/login.php', function (login) {
  $.get('/user.php', function (user) {
    $.get('/info.php', function (info) {
      //代码就这样一层嵌一层,不够直观,维护也麻烦
    });
  });
});

为应对这一挑战,ES6 标准引入了 Promise 机制,旨在优化异步代码的编写方式。

Promise 构造函数与状态管理

Promise 本质上是一个构造函数,利用它可以实例化一个 Promise 对象。


let p = new Promise(function (resolve, reject) {
  setTimeout(() => {
    console.log('OK');
    resolve('OK');
  }, 1000);
});

该构造函数的参数是一个执行函数,该函数接收 resolve 和 reject 两个回调函数作为参数。

其中,resolve 函数用于将 Promise 的状态标记为 fulfilled(已完成),而 reject 函数则用于将状态标记为 rejected(已失败)。

若直接执行上述代码,程序会在运行 1 秒后输出 'OK'。因此需注意,实际使用中通常需要将 Promise 的创建过程封装在函数中,并在外部调用该函数以触发执行。


let p = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(() => {
      console.log('OK');
      resolve('OK');
    }, 1000);
  });
};
p();

then 链式调用与返回值处理

调用上述函数 p() 将返回一个 Promise 实例,该实例提供了 then()、catch() 和 finally() 等常用方法。

then 方法接收两个参数:onFulfilled 和 onRejected,二者均为函数类型。

onFulfilled 用于处理 resolve 传递的成功数据,而 onRejected 用于处理 reject 传递的错误数据。


let p = function () {
  return new Promise(function (resolve, reject) {
    setTimeout(() => {
      if (Math.random() > 0.5) {
        resolve('OK');
      } else {
        reject('ERR');
      }
    }, 1000);
  });
};
p().then(function (data) {
  console.log('fulfilled', data);
}, function (err) {
  console.log('rejected', err);
});

值得注意的是,then() 方法始终返回一个新的 Promise 实例,这使得链式调用成为可能。

在 then 方法的回调中,既可以 return 普通值,也可以 return 另一个 Promise 对象。

若直接 return 一个具体数值,then 方法会返回一个新的 Promise 实例,并以该数值作为参数调用 resolve。


let p = function () {
  return new Promise(function (resolve, reject) {
    resolve(1);
  });
};
p().then(function (data) {
  console.log(`第 ${data} 次调用`);
  //注意这里直接返回的值
  //then会创建一个新的Promise对象,并且以返回的值进行resolve
  //那么该值会被下面的then方法的onFulfilled回调拿到
  return ++data;
}).then(function (data) {
  console.log(`第 ${data} 次调用`);
  return ++data;
}).then(function (data) {
  console.log(`第 ${data} 次调用`);
  return ++data;
});

若 return 的是一个 Promise 对象,其处理逻辑如下所示。


let p = function () {
  return new Promise(function (resolve, reject) {
    resolve(1);
  });
};
p().then(function (data) {
  console.log(`第 ${data} 次调用`);
  return new Promise(function (resolve, reject) {
    resolve(++data);
  });
}).then(function (data) {
  console.log(`第 ${data} 次调用`);
  return new Promise(function (resolve, reject) {
    resolve(++data);
  });
}).then(function (data) {
  console.log(`第 ${data} 次调用`);
  return new Promise(function (resolve, reject) {
    resolve(++data);
  });
});

其最终效果与直接 return 普通数值并无二致。

既然 then() 支持链式操作,我们便可以利用这一特性重构早期的回调地狱写法。


function login() {
  return new Promise(function (resolve, reject) {
    $.get('/login.php', function (result) {
      resolve(result);
    });
  });
}
function user(data) {
  return new Promise(function (resolve, reject) {
    $.get('/user.php', function (result) {
      resolve(result);
    });
  });
}
function info(data) {
  return new Promise(function (resolve, reject) {
    $.get('/info.php', function (result) {
      resolve(result);
    });
  });
}
login().then(function (data) {
  console.log('处理login');
  //把login异步处理获取的数据,传入到下一个处理中。
  return user(data);
}).then(function (data) {
  console.log('处理user');
  //把user异步处理获取的数据,传入到下一个处理中。
  return info(data);
}).then(function (data) {
  console.log('处理info');
});

经过上述改造,原本层层嵌套的回调结构变得清晰易读。需注意的是,以下代码为示意性伪代码。

错误处理与 catch 机制

Promise 对象还内置了 catch 方法,用于捕获执行过程中的错误。该方法在功能上等同于 then(null, onRejected),可视为一种语法糖。


let p = function () {
  return new Promise(function (resolve, reject) {
    resolve('开始');
  });
};
p().then(function (data) {
  console.log('1');
  return new Promise(function (resolve, reject) {
    reject('错误1');
  });
}).then(function (data) {
  console.log('2');
  return new Promise(function (resolve, reject) {
    reject('错误2');
  });
}).then(function (data) {
  console.log('3');
  return new Promise(function (resolve, reject) {
    reject('错误3');
  });
}).catch(function (reason) {
  console.log(reason);
});

需要特别留意的是,一旦异步操作中发生错误,程序流程将跳转至 catch 块,后续未执行的链式操作将不再运行。

Promise 内部已封装 try-catch 机制,当代码运行出错时,会自动生成错误对象并通过 reject 抛出,最终由 catch 方法捕获处理。


let p = function () {
  return new Promise(function (resolve, reject) {
    resolve('开始');
  });
};
p().then(function (data) {
  //注意这里打印了一个未定义的变量
  console.log(a);
}).catch(function (reason) {
  //这里会捕获到错误
  console.log('rejected');
  console.log(reason);
});

静态方法:all、race 及其他

此外,Promise 构造函数还提供了 all()、race()、reject() 和 resolve() 等静态方法,这些方法无需实例化对象即可直接调用。

all() 方法用于并行执行多个异步操作,只有当所有操作均完成后,才会触发最终的回调函数。


function fn1() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('fn1');
    }, 1000);
  });
}
function fn2() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('fn2');
    }, 2000);
  });
}
function fn3() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('fn3');
    }, 3000);
  });
}
//all会等待所有操作完成,会把所有操作的结果放到一个数组中,传给then。
Promise.all([fn1(), fn2(), fn3()]).then(function (data) {
  console.log(data);
});

race() 方法遵循“竞速”原则,即哪个异步操作最先完成,就以该结果为准,并将结果传递给 then 方法。


function fn1() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('fn1');
    }, 1000);
  });
}
function fn2() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('fn2');
    }, 2000);
  });
}
function fn3() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('fn3');
    }, 3000);
  });
}
//race是以谁先处理完,就以谁为准,fn1最先处理完,那fn1的结果会传给then
//注意这里fn2和fn3还是会执行,不过不会进入then了
Promise.race([fn1(), fn2(), fn3()]).then(function (data) {
  console.log(data);
});

reject() 方法返回一个带有指定拒绝原因(reason)参数的 Promise 对象。


// Promise.reject('错误')
// 等同于
// new Promise(function(resolve, reject) {
//   reject('错误');
// });
let p = Promise.reject('错误');
p.then(function (data) {
}).catch(function (reason) {
  console.log(reason);
});

resolve() 方法根据传入的值,返回一个对应状态的 Promise 对象。


//如果传入的参数是普通值,则返回一个新Promise对象,并以该值resolve
let p1 = Promise.resolve('OK');
p1.then(function (data) {
  console.log(data);
});
//如果传入的参数是一个Promise对象,则原封不动的返回该Promise对象
let obj = new Promise(function (resolve, reject) {
  resolve('我是Promise对象');
});
let p2 = Promise.resolve(obj);
p2.then(function (data) {
  console.log(data);
  console.log(p2 === obj);
});
//如果传入的参数是一个thenable对象(带有then方法),
//会转换成Promise对象,并执行thenable对象的then方法
let then = {
  then(resolve, reject) {
    resolve('我是thenable对象');
  }
}
let p3 = Promise.resolve(then);
p3.then(function (data) {
  console.log(data);
});
//如果什么参数都不传入,则返回状态为resolved的Promise对象
let p4 = Promise.resolve();
p4.then(function (data) {
  console.log(data);
}).catch(function (reason) {
  console.log(reason);
});

感兴趣的用户可使用在线 HTML/CSS/JavaScript 代码运行工具:http://tools.jb51.net/code/HtmlJsRun 对上述代码的实际运行效果进行测试。

如需深入了解 JavaScript 其他相关知识,可查阅本站系列专题:《JavaScript操作DOM技巧总结》、《JavaScript页面元素操作技巧总结》、《JavaScript事件相关操作与技巧大全》、《JavaScript查找算法技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript错误与调试技巧总结》。

希望本文关于 JavaScript 程序设计的阐述能对大家有所帮助。

作者最新文章