这次聊聊Promise对象

51次阅读

共计 4648 个字符,预计需要花费 12 分钟才能阅读完成。

欢迎大家前往腾讯云 + 社区,获取更多腾讯海量技术实践干货哦~
本文由前端林子发表于云 + 社区专栏

Promise 是 CommonJS 提出的一种规范,在 ES6 中已经原生支持 Promise 对象,非 ES6 环境可以用 Bluebird 等库来支持。
0. 引入
在 js 中任务的执行模型有两种:同步模式和异步模式。
同步模式:后一个任务 B 等待前一个任务 A 结束后,再执行。任务的执行顺序和任务的排序顺序是一致的。
异步模式:每一个任务有一个或多个回调函数,前一个任务 A 结束后,不是执行后一个任务 B,而是执行任务 A 的回调函数。而后一个任务 B 是不等任务 A 结束就执行。任务的执行顺序,与任务的排序顺序不一致。
异步模式编程有四种方法:回调函数(最基本的方法,把 B 写成 A 的回调函数)、事件监听(为 A 绑定事件,当 A 发生某个事件,就执行 B)、发布 / 订阅,以及本文要介绍的 Promise 对象。
Promise 是一个用于处理异步操作的对象,可以将回调函数写成链式调用的写法,让代码更优雅、流程更加清晰,让我们可以更合理、更规范地进行异步处理操作。它的思想是,每一个异步任务返回一个 Promise 对象,该对象有一个 then 方法,允许指定回调函数。
1.Promise 的基本知识
1.1 三种状态
Pending:进行中,刚创建一个 Promise 实例时,表示初始状态;
resolved(fulfilled):resolve 方法调用的时候,表示操作成功,已经完成;
Rejected:reject 方法调用的时候,表示操作失败;
1.2 两个过程
这三种状态只能从 pendeng–>resolved(fulfilled),或者 pending–>rejected,不能逆向转换,也不能在 resolved(fulfilled) 和 rejected 之间转换。并且一旦状态改变,就不会再改变,会一直保持这个结果。
汇总上述,创建一个 Promise 的实例是这样的:
// 创建 promise 的实例
let promise = new Promise((resolve,reject)=>{
// 刚创建实例时的状态:pending

if(‘ 异步操作成功 ’){
// 调用 resolve 方法,状态从 pending 变为 fulfilled
resolve();
}else{
// 调用 reject 方法,状态从 pending 变为 rejected
reject();
}
});
1.3 then()
用于绑定处理操作后的处理程序,分别指定 fulfilled 状态和 rejected 状态的回调函数,即它的参数是两个函数,第一个用于处理操作成功后的业务,第二个用于处理操作失败后的业务。
//then()
promise.then((res)=> {
// 处理操作成功后的业务(即 Promise 对象的状态变为 fullfilled 时调用)
},(error)=> {
// 处理操作失败后的业务(即 Promise 对象的状态变为 rejected 时调用)
});
1.4 catch()
用于处理操作异常的程序,catch() 只接受一个参数
//catch()
promise.catch((error)=> {
// 处理操作失败后的业务
});
一般来说,建议不要在 then() 里面定义 rejected 状态的回调函数,而是将 then() 用于处理操作成功,将 catch() 用于处理操作异常。因为这样做可以捕获 then() 执行中的错误,也更接近同步中 try/catch 的写法:
//try-catch
// bad
promise.then((res)=> {
// 处理操作成功后的业务
}, (error)=> {
// 处理操作失败后的业务
});

// good
promise
.then((res)=> {
// 处理操作成功后的业务
})
.catch((error)=> {
// 处理操作失败后的业务
});
1.5 all()
接受一个数组作为参数,数组的元素是 Promise 实例对象。只有当参数中的实例对象的状态都为 fulfilled 时,Promise.all() 才会有返回。
实例代码(可直接在浏览器中打开):
<!DOCTYPE html>
<html lang=”en”>

<head>
<meta charset=”UTF-8″>
<title>Promise 实例 </title>
<style type=”text/css”></style>
<script type=”text/javascript”>
window.onload = () => {
// 创建实例 promise1
let promise1 = new Promise((resolve) => {
setTimeout(() => {
resolve(‘promise1 操作成功 ’);
console.log(‘1’)
}, 3000);
});

// 创建实例 promise1
let promise2 = new Promise((resolve) => {
setTimeout(() => {
resolve(‘promise1 操作成功 ’);
console.log(‘2’)
}, 1000);
});

Promise.all([promise1, promise2]).then((result) => {
console.log(result);
});
}
</script>
</head>

<body>
<div></div>
</body>

</html>
结果(注意看时间):
Promise.all()
代码说明:
1s 后,promise2 进入 fulfilled 状态,间隔 2s,也就是 3s 后,promise1 也进入 fulfilled 状态。这时,由于两个实例都进入了 fulfilled 状态,所以 Promise.all() 才进入了 then 方法。
使用场景:执行某个操作需要依赖多个接口请求回的数据,且这些接口之间不存在互相依赖的关系。这时使用 Promise.all(),等到所有接口都请求成功了,它才会进行操作。
1.6 race()
和 all() 的参数一样,参数中的 promise 实例,只要有一个状态发生变化(不管是成功 fulfilled 还是异常 rejected),它就会有返回,其他实例中再发生变化,它也不管了。
实例代码(可直接在浏览器中打开):
<!DOCTYPE html>
<html lang=”en”>

<head>
<meta charset=”UTF-8″>
<title>Promise 实例 </title>
<style type=”text/css”></style>
<script type=”text/javascript”>
window.onload = () => {
// 创建实例 promise1
let promise1 = new Promise((resolve) => {
setTimeout(() => {
resolve(‘promise1 操作成功 ’);
console.log(‘1’)
}, 3000);
});

// 创建实例 promise1
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(‘promise1 操作失败 ’);
console.log(‘2’)
}, 1000);
});

Promise.race([promise1, promise2])
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
})
}
</script>
</head>

<body>
<div></div>
</body>

</html>
结果(注意看时间):
Promise.race()
代码说明:
1s 后,promise2 进入 rejected 状态,由于一个实例的状态发生了变化,所以 Promise.race() 就立刻执行了。
2 实例
平时开发中可能经常会遇到的问题是,要用 ajax 进行多次请求。例如现在有三个请求,请求 A、请求 B、请求 C。请求 C 要将请求 B 的请求回来的数据做为参数,请求 B 要将请求 A 的请求回来的数据做为参数。
按照这个思路,我们可能会直接写出这样的层层嵌套的代码:
//—— 请求 A 开始 ———
$.ajax({
success:function(res1){

//—— 请求 B 开始 —-
$.ajax({
success:function(res2){

//—- 请求 C 开始 —
$.ajax({
success:function(res3){
}
});
//— 请求 C 结束 —

}
});
//—— 请求 B 结束 —–

}
});
//—— 请求 A 结束 ———
在请求 A 的 success 后,请求 B 发送请求,在请求 B 的 success 后,请求 C 发送请求。请求 C 结束后,再向上到请求 B 结束,请求 B 结束后,再向上到请求 A 结束。
这样虽然可以完成任务,但是代码层层嵌套,代码可读性差,也不便于调试和后续的代码维护。而如果用 Promise,你可以这样写(示意代码,无 ajax 请求):
此处附上完整可执行代码,可在浏览器的控制台中查看执行结果:
<!DOCTYPE html>
<html lang=”en”>

<head>
<meta charset=”UTF-8″>
<title>Promise 实例 </title>
<style type=”text/css”></style>
<script type=”text/javascript”>
window.onload = () => {
let promise = new Promise((resolve, reject) => {

if (true) {
// 调用操作成功方法
resolve(‘ 操作成功 ’);
} else {
// 调用操作异常方法
reject(‘ 操作异常 ’);
}
});

//then 处理操作成功,catch 处理操作异常
promise.then(requestA)
.then(requestB)
.then(requestC)
.catch(requestError);

function requestA() {
console.log(‘ 请求 A 成功 ’);
return ‘ 下一个是请求 B ’;
}
function requestB(res) {
console.log(‘ 上一步的结果:’ + res);
console.log(‘ 请求 B 成功 ’);
return ‘ 下一个是请求 C ’;
}
function requestC(res) {
console.log(‘ 上一步的结果:’ + res);
console.log(‘ 请求 C 成功 ’);
}
function requestError() {
console.log(‘ 请求失败 ’);
}
}
</script>
</head>

<body>
<div></div>
</body>

</html>
结果如下:
实例
可以看出请求 C 依赖请求 B 的结果,请求 B 依赖请求 A 的结果,在请求 A 中是使用了 return 将需要的数据返回,传递给下一个 then() 中的请求 B,实现了参数的传递。同理,请求 B 中也是用了 return,将参数传递给了请求 C。
3. 小结
本文主要介绍了 Promise 对象的三个状态和两个过程。“三个状态”是:初始化、操作成功、操作异常,“两个过程”是初始化状态到操作成功状态,和初始化状态到操作异常状态。除此之前,还有两种实例方法:then()、catch() 来绑定处理程序。类方法:Promise.all()、Promise.race()。如有问题,欢迎指正。

相关阅读【每日课程推荐】机器学习实战!快速入门在线广告业务及 CTR 相应知识

正文完
 0