JavaScript Promise 用法详解与注意事项
本文深入解析 JavaScript 中 Promise 对象的创建、状态流转(Pending、Fulfilled、Rejected)及链式调用机制。详细阐述 then 方法返回值的判定规则、catch 方法的错误捕捉限制,以及解决回调地狱的实际应用。通过代码示例说明 Promise 在异步操作中的优势与注意事项,帮助开发者掌握现代 JS 异步编程的核心技术。
一、Promise 概述
Promise 是 JavaScript 语言中标准的内置对象,主要用于表示一个异步操作的最终结果(成功或失败)及其对应的值。它允许开发者将异步操作成功或失败的原因与相应的处理程序关联起来,从而自定义异步操作完成后的行为。通过 Promise,异步方法的调用方式变得与同步方法类似,具备返回值特性。不同的是,它立即返回一个 Promise 对象,而非最终结果;当 Promise 的状态发生变化时,才会触发对应的处理程序。
Promise 对象在任何时刻必然处于以下三种状态之一:1. 待定(Pending);2. 已兑现(Fulfilled);3. 已拒绝(Rejected)。
Promise 在创建之初处于待定状态,此时它既未被兑现也未被拒绝。它通常用于包装尚未支持 Promise 的函数,将其转换为异步操作函数。当异步操作结束时,会进入两个分支之一:已兑现或已拒绝,如下图所示。处于已兑现状态的 Promise 会调用后续的 then 方法,而处于已拒绝状态的 Promise 既可以通过 then 方法处理(如果提供了拒绝回调),也可以被 catch 方法捕获。关于 then 方法为何能处理两种状态,将在后文详细展开。

二、Promise 的流程走向
上图展示了 Promise 的流程图。从左至右可以看出,当 Promise 处于待定状态时,通过两个分支分别进入 then 方法或 catch 方法。这两个方法都会返回一个新的 Promise 对象。如果这个新返回的 Promise 也被敲定(即状态变为已兑现或已拒绝),且其后仍有 then 或 catch 方法,流程将继续进入后续的 then 或 catch 中,直到没有后续处理为止。
这意味着,Promise 与 then 或 catch 可以形成异步操作的链式结构。结合 JavaScript 的事件循环机制,这种设计使得 JS 的优势更加显著,至今仍在浏览器开发中广泛使用。
我们需要特别注意流程的几个关键特点:
1. Promise 是对象,其状态转移成功与否不取决于返回值,而取决于状态是否被敲定(即变为已兑现或已拒绝)。
2. then 和 catch 是函数。只要 Promise 的状态发生改变,该 Promise 对应的后续 then 或 catch 方法就会被调用。这两个方法本身会返回一个新的 Promise 对象,这个返回的 Promise 又会决定后续 then 或 catch 方法的执行。无论 then 还是 catch,其返回值一定是 Promise 对象。
三、Promise 的创建
Promise实例通过new关键字及构造函数构建。构造函数接收一个“处理器函数”(executor function)作为参数,该函数接受resolve和reject两个回调函数。异步任务成功完成并返回值时调用resolve;任务失败且返回原因(通常为错误对象)时调用reject。示例如下:
let myFirstPromise = new Promise(function(resolve, reject){
//当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
//在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
setTimeout(function(){
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function(successMessage){
//successMessage的值是上面调用resolve(...)方法传入的值.
//successMessage参数不一定非要是字符串类型,这里只是举个例子
console.log("Yay! " + successMessage);
});
四、Promise 的优势
在 Promise 出现之前,调用异步代码块时无法保证执行顺序。如果存在异步代码结果按序处理的需求,该如何实现?
过去通常通过层层嵌套异步代码来实现顺序执行,但这导致了代码维护困难,开发难度显著增加。
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
这就是经典的“回调地狱”问题。而使用前面介绍的 Promise 后,代码可以转化为易于维护的链式结构。
五、then 方法返回的 Promise 类型
当一个 Promise 完成(fulfilled)或者失败(rejected)时,返回函数将被异步调用(由当前的线程循环来调度完成)。具体的返回值依据以下规则确定。如果 then 中的回调函数:
- 返回了一个值,那么
then返回的 Promise 将会成为接受状态(Fulfilled),并且将返回的值作为接受状态回调函数的参数值。 - 没有返回任何值,那么
then返回的 Promise 将会成为接受状态,并且该接受状态回调函数的参数值为undefined。 - 抛出一个错误,那么
then返回的 Promise 将会成为拒绝状态(Rejected),并且将抛出的错误作为拒绝状态回调函数的参数值。 - 返回一个已经是接受状态的 Promise,那么
then返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态回调函数的参数值作为该被返回的 Promise 的接受状态回调函数的参数值。 - 返回一个已经是拒绝状态的 Promise,那么
then返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态回调函数的参数值作为该被返回的 Promise 的拒绝状态回调函数的参数值。 - 返回一个未定状态(
pending)的 Promise,那么then返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。
六、catch 捕捉的错误
catch 能捕捉 Promise 组合中出现的错误,但是有两种错误不能捕捉:
1. 已决议(Resolved)的错误不能捕捉。
//创建一个新的 Promise ,且已决议
var p1 = Promise.resolve("calling next");
var p2 = p1.catch(function (reason) {
//这个方法永远不会调用
console.log("catch p1!");
console.log(reason);
});
p2.then(function (value) {
console.log("next promise's onFulfilled"); /* next promise's onFulfilled */
console.log(value); /* calling next */
}, function (reason) {
console.log("next promise's onRejected");
console.log(reason);
});
2. 异步函数中抛出的错误不能捕捉。
需要注意的是,作者通过实践发现:Promise 包裹的异步函数执行成功后,必须显式地调用 resolve 和 reject 方法才能触发后续的 then 和 catch 方法。如果 Promise 包裹的不是异步函数,而是普通的同步函数,当该同步代码运行出错时,即便没有调用 reject 方法,后续的 catch 方法仍然能够捕捉到这个错误;但如果同步代码没有出错,且没有显式调用 resolve 方法转移状态的话,后续的 then 方法不会触发。
七、高级示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 10px;
}
html{
width: 100%;
height: 100%;
}
body{
width: 100%;
height: 100%;
display:flex;
align-items: center;
justify-content: center;
}
div.displaydatabox{
width: 300px;
height: 300px;
border-radius: 50px;
text-align: center;
line-height: 300px;
box-shadow: 0 0 10px 2px black;
}
div.button{
width: 100px;
height: 50px;
border-radius: 21px;
border: 2px solid orange;
line-height: 50px;
text-align: center;
cursor: pointer;
}
</style>
</head>
<body>
<div class="button">创建</div>
<div class="button">输入文字</div>
<div class="button">消失</div>
<script lang="javascript">
let buttonlist=document.querySelectorAll("div.button");
let body=document.querySelector("body");
buttonlist[0].onclick=function()
{
let div=document.createElement("div");
div.className="displaydatabox";
body.appendChild(div);
}
buttonlist[2].onclick=function()
{
let div=document.querySelector("div.displaydatabox");
body.removeChild(div);
}
buttonlist[1].onclick=function(e)
{
let p1=new Promise((resolve,reject)=>
{
setTimeout(()=>{//用setTimeout函数模拟异步函数
let div=document.querySelector("div.displaydatabox");
div.textContent="这是promise实验";
//reject(1);
resolve(1);//调用resolve将调用第一个then
},2000);
}).then(function(resolve){
return new Promise((resolve,reject)=>{
console.log("这是状态咩有敲定的promise,所以不会调用后面所有的then方法"); //resolve(1)//没有调用resolve或者reject,所以状态未敲定。如果调用,将输出1和finally it has been called!!
})
.then(function(e){
console.log(e);
});
}).catch(function(e)
{
console.log(e+" 没有块可以输入!!");
}).then(()=>
{
console.log("finally it has been called!!");
})
}
</script>
</body>
</html>
到此这篇关于 JavaScript 中 Promise 的用法和注意事项的文章就介绍到这了,更多相关 JS 中 Promise 用法内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
JS解析Excel后怎么将数据转换为JSON格式
2026-11-04 20:58:28
