前世今生:
Promise 在 ECMAScript 2015( ES6 )中发布,在 ES6 发布之前,Promise是由 Jesse MacFadyen 在 2008 年提出的。他在一篇名为《Promise/A+》的文章中提出了 Promise 的规范草案,后来被业界广泛的应用。
后来在 ES6 的开发中, ECMAScript 标准委员会(ECMA International)将这一规范纳入了 ES6 的标准中。
本篇文章的任务:
- 理解Promise A+规范的基本概念
- 学会创建Promise
- 学会针对Promise进行后续处理
戴夫的烦恼
这里我们引用了袁教头在解释Promise这一东东时所用的案例
戴夫心中有很多女神,他今天下定决心,要向这些女神表白,他认为,只要女神够多,根据概率学原理,总有一个会接收他
为了稳妥起见,戴夫就决定使用串行的方式进行表白:先给第1位女神发送短信,然后等待女神的回应,如果成功了,就结束,如果失败了,则再给第2位女神发送短信,依次类推,我们用图来解释
戴夫的女神一共有4位,名字分别是:坚果、豌豆射手、雪花豌豆、小海豚
发短信是一个重复性的劳动,但是戴夫他是个程序员,因此决定用函数封装来完成这个体力活
/**
* 像某位女神发送一则表白信
* @param {*} name 女神的姓名
* @param {*} onFulffiled 成功后的回调
* @param {*} onRejected 失败后的回调
*/
function sendMessage(name, onFulffiled, onRejected) {
console.log(`戴夫 -> ${name}: 我不知道未来会怎样,但我知道我想和你一起经历。你愿意和我一起探索未知吗?`)
console.log(`等待${name}的回复`)
// 模拟 女神回复需要一定的时间
setTimeout(() => {
// 模拟 有10%的几率成功
if (Math.random() <= 0.1) {
// 成功,调用 onFuffiled,并传递女神的回复
onFulffiled(`${name} -> 戴夫:我愿意😘`);
} else {
// 失败,调用 onRejected,并传递女神的回复
onRejected(`${name} -> 戴夫:你是个好人😜`);
}
}, 1000)
}
有了这个函数之后,戴夫就开始写程序给每个女神发短信了
// 首先 戴夫 先跟坚果发送了短信
sendMessage(
"坚果",
(reply) => {
// 如果成功了,输出回复的消息
console.log(reply);
},
(reply) => {
// 失败了,输出回复的消息后,向 豌豆射手 发送消息
console.log(reply);
sendMessage(
"豌豆射手",
(reply) => {
// 如果成功了,输出回复的消息
console.log(reply);
},
(reply) => {
// 如果失败了,输出回复的消息, 向 雪花豌豆 发送消息
console.log(reply);
sendMessage(
"雪花豌豆",
(reply) => {
// 成功了,输出回复的消息
console.log(reply);
},
(reply) => {
// 如果失败了,输出回复的消息, 向 小海豚 发送消息
console.log(reply);
sendMessage(
"小海豚",
(reply) => {
// 成功了,输出回复的消息 结束
console.log(reply);
},
(reply) => {
// 如果还是失败了,戴夫将要孤独终老了
console.log(reply);
console.log("戴夫的女神,都嫌弃戴夫没修胡子,把戴夫都拒绝了");
}
);
}
);
}
);
}
);
戴夫把程序写完之后,分享给了他的朋友看,戴夫的朋友是一个完美主义者,他看完之后的内心时崩溃的。
在这一层有一层的回调中,犹如托塔李天王的玲珑塔一般,所以,就形成了 传说中的「回调地狱 callback hell」。
那我们需要如何去解决,这一座宝塔呢?
这就要用到了 Promise
Promise规范
在MDN中是这样解释的, 他将Promise 比作一个代理,用来代表一个在创建 promise 时不一定已知的值。它允许你将处理程序与异步操作的最终成功值或失败原因关联起来。这使得异步方法可以像同步方法一样有返回值:异步方法不会立即返回最终值,而是返回一个 promise,以便在将来的某个时间点提供该值。
但在我的理解中,Promise是一套专门处理异步场景的规范,它能有效的避免回调地狱的产生,使异步代码更加清晰、简洁、统一
这套规范最早诞生于前端社区,由 Jesse MacFadyen 在 2008 年提出,规范名称为Promise A+
该规范出现后,立即得到了业界很多开发者的响应
Promise A+ 规定
-
所有的异步场景,都可以看作是一个异步任务,每个异步任务,在 JS 中应该表现为一个对象,该对象称之为Promise对象,也叫做任务对象
-
每个 Promise 对象都会有3个状态
我们将上面的图简化一下:
Promise 的逻辑关系:
- 总是由未决状态变为已决状态,无法逆行
- 任务总会从挂起(pending)到完成(fulfilled)或失败(rejected),在已决状态中也只会由完成跟失败。无法逆行
- 在任务执行完毕之后,也就是确定为完成或失败的一种,任务就不会返回去重新执行;任务状态就永远无法改变。
-
Promise 任务由
挂起 -> 完成
称为resolve
;由挂起 -> 失败
称为reject
。在任务的执行中,不管是 成功还是失败他都允许你携带一个形参。任务完成时可能有一个相关的数据,任务失败时,可能是一个失败的原因。
- Promise 的 Thenable 接口,该接口允许我们针对任务进行后续的操作,在针对完成状态进行的后续操作称为
onFulfilled
,对于失败状态的后续操作称为onRejected
Promise Api
在ES6中,ECMAScript 标准委员会为我们实现了一整套的API,用来帮助我们实现 Promise 的A+规范
Promise 构造函数
Promise 构造函数接受一个执行器函数作为参数,该函数接受两个参数:resolve
和 reject
。resolve
和 reject
是两个函数,用于改变 Promise 的状态。
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve('Operation successful');
} else {
reject('Operation failed');
}
}, 1000);
});
Promise 方法
1. .then()
.then()
方法接受两个回调函数作为参数:第一个回调函数在 Promise 被 resolve
时调用,第二个回调函数在 Promise 被 reject
时调用(可选)。
promise
.then(result => console.log(result))
.catch(error => console.error(error));
2. .catch()
.catch()
方法用于处理 Promise 被 reject
时产生的错误。
promise
.then(result => console.log(result))
.catch(error => console.error(error));
3. .finally()
.finally()
方法用于无论 Promise 的结果如何都会执行的回调函数。
promise
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('Finally, the operation is complete'));
静态方法
1. Promise.resolve()
Promise.resolve()
方法用于立即创建一个已解决的 Promise。
const immediateSuccess = Promise.resolve('Immediate success');
immediateSuccess.then(result => console.log(result));
2. Promise.reject()
Promise.reject()
方法用于立即创建一个已拒绝的 Promise。
const immediateFailure = Promise.reject('Immediate failure');
immediateFailure.catch(error => console.error(error));
3. Promise.all()
Promise.all()
方法用于等待所有的 Promise 都完成(resolve)。
const promises = [
fetch('https://api.example.com/data1'),
fetch('https://api.example.com/data2')
];
Promise.all(promises)
.then(responses => Promise.all(responses.map(r => r.json())))
.then(results => console.log(results))
.catch(error => console.error('Error fetching data:', error));
4. Promise.allSettled()
Promise.allSettled()
方法用于等待所有的 Promise 都完成,无论它们是 resolved 还是 rejected。
const promises = [
fetch('https://api.example.com/data1'),
fetch('https://api.example.com/data2')
];
Promise.allSettled(promises)
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('Fulfilled:', result.value);
} else {
console.error('Rejected:', result.reason);
}
});
});
5. Promise.race()
Promise.race()
方法用于等待第一个完成的 Promise。
const promises = [
fetch('https://api.example.com/data1'),
fetch('https://api.example.com/data2')
];
Promise.race(promises)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error fetching data:', error));
异步函数 (async/await
)
async/await
是基于 Promise 的一种更简洁的写法,它可以使异步代码看起来更像同步代码。
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();