本文系统解析ES6中Promise的核心用法与底层原理。详细讲解Promise的三种状态(Pending、Fulfilled、Rejected)及其转换机制,阐述resolve与reject函数的实际作用。通过代码示例演示then、catch、all及race方法的使用,并深入剖析链式调用与错误处理机制,帮助开发者掌握异步编程的最佳实践。
Promise基础概念与初始化
Promise是ES6标准中极为重要的特性之一,本文将对其具体用法及底层实现原理进行系统且细致的梳理。
基于常规理解,Promise是一个构造函数,内部封装了all、resolve、reject、then、catch等方法,通常仅在处理异步操作场景时被调用。
接下来,我们将通过实例化一个Promise对象,并在其中执行异步操作来展开说明:
// 使用Promise的时候一般会把它包裹在一个函数中,并在函数的最后返回这个Promise对象
function runPro()(
var a = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('work done!');
resolve('success');
}, 1000);
});
return a;
)
runPro()
在上述代码中,Promise构造函数接收一个箭头函数作为参数,该箭头函数又接收resolve和reject两个参数。我们在箭头函数内部利用setTimeout执行异步逻辑,并在异步操作完成后调用resolve方法,同时传入字符串'success'作为参数。
运行该代码,由于setTimeout设置的延迟时间为1000毫秒,因此等待1秒后控制台会输出'work done!'。
此时尚看不出Promise相较于普通异步回调的特殊之处,resolve和reject的具体作用也未完全显现。
前文提及Promise构造函数上挂载了then和catch方法。在上述代码片段中,runPro函数最终return了一个Promise对象,因此我们可以在runPro执行完毕后,通过then方法对该Promise对象进行后续处理:
runPro().then((res) => {
console.log('then:', res);
//TODO something
});
输出结果如下:

在runPro返回的Promise对象上直接调用then方法,then接收一个函数作为参数A,该箭头函数接收参数B,其值即为前文resolve方法传递的字符串'success'。
执行代码,1秒后首先输出'work done!',随后紧接着输出'then: success'。
此时可初步体现Promise的价值:前文代码中的then方法类似于Promise的回调函数,当Promise内部的异步操作完成后,通过链式调用执行该回调函数。
关键点在于链式调用,当实际业务涉及多层嵌套回调时,Promise的优势才能充分展现:
function runPro2(){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
console.log('this is runPro2');
}, 1000);
});
return a;
};
function runPro3(){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
console.log('this is runPro3');
}, 1000);
});
return a;
};
runPro().then(() => {
return runPro2();
}).then(() => {
return runPro3();
}).then(() => {
console.log('all done')
})
Promise状态机与核心原理
至此,我们大致理解Promise是一种在异步操作完成后执行回调函数的结构体。但其内部原理仍显模糊,特别是resolve和reject这两个参数尚未完全厘清,前几个代码片段中虽调用了resolve,但其具体功能未得到充分体现。
关于resolve和reject,以往的理解是:异步操作成功时调用resolve,失败时调用reject。但后来发现这种理解并不准确。
在正确理解这两个函数之前,必须首先明确Promise的一个重要特性:状态。
Promise的状态机制如下:
一个Promise对象的当前状态必须处于以下三种状态之一:等待(Pending)、完成(Fulfilled)、拒绝(Rejected)。
Pending:
在异步操作完成之前,Promise处于等待状态,此时Promise可以迁移至Fulfilled或Rejected状态。
Fulfilled:
异步操作完成后,Promise可能从Pending状态迁移至Fulfilled状态。处于Fulfilled状态的Promise必须拥有一个不可变的终值,且一旦进入该状态,便不能再迁移至其他状态。
Rejected:
异步操作完成后,Promise可能从Pending状态迁移至Rejected状态。处于Rejected状态的Promise必须拥有一个不可变的拒绝原因,且一旦进入该状态,便不能再迁移至其他状态。
了解了Promise的三种状态后,我们再来看resolve和reject这两个函数的具体作用:
- resolve函数负责将Promise的状态设置为Fulfilled,reject函数负责将Promise的状态设置为Rejected。
- 当状态被设置为Fulfilled或Rejected后,即表示Promise内部的异步操作执行完毕,此时程序将执行then回调函数。
- resolve和reject函数传递的参数,将由then函数中的箭头函数接收。
实际上,理解Promise的关键在于状态机制,通过维护状态并传递状态来实现及时的回调响应。
Promise使用关键注意事项
因此,如下面代码所示,在使用Promise进行异步操作时,有几个关键点需要特别注意:
在一个函数中new出一个Promise对象后,函数的最后必须将该Promise对象return出来,否则该函数无法使用then方法进行回调;
异步操作中必须执行resolve或reject函数,否则该Promise将一直处于Pending状态,代码不会执行其回调函数。
function runPro(){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolve');
console.log('this is runPro');
}, 1000);
});
return a;
}
runPro().then((res) => {
console.log(res);
})
同理,当异步操作执行失败时,代码通过执行reject函数,将Promise的状态设置为Rejected,并返回一个拒绝原因:
function runPro(item){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
if(item >= 18) {
console.log('item 大于 18');
resolve('一切正常!');
}else {
console.log('item 小于 18');
reject('18+电影不允许放映!');
}
}, 1000);
});
return a;
}
runPro(13)
.then((res) => {
console.log('resolve:',res);
},(rej) => {
console.log('reject:', rej);
})
我们给runPro函数传递不同的参数,runPro接收参数后进行异步判断,如果参数值小于18,执行reject函数,反之则执行resolve函数。异步操作完成后,执行then回调函数,这里的回调函数可以接收两个箭头函数作为参数,分别对应resolve函数的回调和reject函数的回调,这两个箭头函数可以分别获取resolve和reject传递的参数。
如下面截图所示,分别给runPro传递两个不同值后,得到了两种不同的结果:

错误处理:catch函数的应用
catch函数的用法
在Promise中,catch函数可以替代reject函数使用,用于指定接收reject的回调:
function runPro(item){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
if(item >= 18) {
console.log('item 大于 18');
resolve('一切正常!');
}else {
console.log('item 小于 18');
reject('18+电影不允许放映!');
}
}, 1000);
});
return a;
}
runPro(13)
.then((res) => {
console.log('resolve:',res);
})
.catch((rej) => {
console.log('catch:', rej);
})
如上面代码所示,对调函数then只有一个箭头函数作为参数,这种情况下,该箭头函数被指定用来接收resolve函数的回调,而reject函数的回调则由catch函数来接收:

此处使用catch函数来接收reject的回调有一个优点,当前面的then回调函数中出现位置错误时,catch函数可以对错误信息进行处理,而不会导致代码报错。这个原理和常用的try/catch语句相同。
function runPro(item){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
if(item >= 18) {
console.log('item 大于 18');
resolve('一切正常!');
}else {
console.log('item 小于 18');
reject('18+电影不允许放映!');
}
}, 1000);
});
return a;
}
runPro(19)
.then((res) => {
console.log(adc); // 这里的adc是一个未定义的变量,当代码执行到这里时,会抛出Error信息导致代码卡死
console.log('resolve:',res);
}, (rej) => {
console.log('reject:',rej);
});
runPro(19)
.then((res) => {
console.log(abc); // 这里的abc是一个未定义的变量,但是由于后边使用.catch函数进行了异常捕获,所以程序不会报错。而且错误原因也会作为参数传递到后面.catch函数的参数中
console.log('resolve:',res);
})
.catch((rej) => {
console.log('catch:', rej);
})
并行异步操作:all与race函数
all函数 / race函数并行异步操作
Promise的all函数和race函数都提供了并行异步操作的能力,二者的区别在于,当这些并行的异步操作耗时不同时,all函数是在所有的异步操作都执行完之后才会执行,而race函数则会在第一个异步操作完成之后立即执行。
function runPro1(){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
console.log('this is runPro1');
}, 1000);
});
return a;
}
function runPro2(){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
console.log('this is runPro2');
}, 2000);
});
return a;
};
function runPro3(){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
console.log('this is runPro3');
}, 3000);
});
return a;
};
Promise.all([runPro1(), runPro2(), runPro3()])
.then((res) => {
console.log('all:', res);
})

function runPro1(){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
console.log('this is runPro1');
}, 1000);
});
return a;
}
function runPro2(){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
console.log('this is runPro2');
}, 2000);
});
return a;
};
function runPro3(){
var a = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
console.log('this is runPro3');
}, 3000);
});
return a;
};
Promise.races([runPro1(), runPro2(), runPro3()])
.then((res) => {
console.log('all:', res);
})

如上两个代码片段所示,all函数和race都接收一个数组作为参数,这个数组中的值就是我们要进行并行执行的异步操作。这里我们同样使用then函数作为异步操作完成的回调函数。
同时我们通过console输出发现,在race函数的回调函数开始执行的时候,另外两个没有执行完成的异步操作并没有停止,依旧在执行。
到此这篇关于JavaScript中Promise的简单使用及其原理详解的文章就介绍到这了,更多相关JavaScript Promise内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!