背景
在JavaScript中,Promise 是一个代表了异步操作最终完成或失败的对象。它允许你以同步的方式(即使用 .then()、.catch() 和 .finally() 等方法)来处理异步操作的结果。Promise 的出现极大地简化了异步编程的复杂性,特别是在处理多个异步操作时。
基本概念
- 状态:一个
Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。 - 解决(Resolve) :当异步操作成功完成时,
Promise被解决(fulfilled),并将结果作为参数传递给.then()方法中的回调函数。 - 拒绝(Reject) :当异步操作失败时,
Promise被拒绝(rejected),并将错误作为参数传递给.catch()方法中的回调函数。
创建 Promise
你可以使用 new Promise() 构造函数来创建一个新的 Promise 对象。
这个构造函数接受一个执行器(executor)函数作为参数,该函数本身又接受两个函数作为参数:resolve 和 reject。
let promise = new Promise(function(resolve, reject) {
// 异步操作
if (/* 操作成功 */) {
resolve(value); // 将Promise的状态从pending变为fulfilled,并将value作为结果
} else {
reject(error); // 将Promise的状态从pending变为rejected,并将error作为原因
}
});
使用 Promise
你可以使用 .then() 方法来处理 Promise 成功的情况,使用 .catch() 方法来处理失败的情况,还可以使用 .finally() 方法来指定无论 Promise 最终状态如何都会执行的操作。
1、基本使用
promise.then(function(value) {
// 处理成功的情况
console.log(value);
}).catch(function(error) {
// 处理失败的情况
console.error(error);
}).finally(function() {
// 无论成功或失败都会执行
console.log('Promise执行完成');
});
2、Promise封装读取文件
(1)非Promise封装
//1. 引入 fs 模块
const fs = require('fs');
//2. 调用方法读取文件
fs.readFile('./resources/为学.md', (err, data)=>{
//如果失败, 则抛出错误
if(err) throw err;
//如果没有出错, 则输出内容
console.log(data.toString());
});
(2)Promise封装
const fs = require('fs');
const p = new Promise(function(resolve, reject){
fs.readFile("./resources/为学.mda", (err, data)=>{
//判断如果失败
if(err) reject(err);
//如果成功
resolve(data);
});
});
p.then(function(value){
console.log(value.toString());
}, function(reason){
console.log("读取失败!!");
});
3、PromiseAJAX封装
const p = new Promise((resolve, reject) => {
//1. 创建对象
const xhr = new XMLHttpRequest();
//2. 初始化
xhr.open("GET", "https://api.apiopen.top/getJ");
//3. 发送
xhr.send();
//4. 绑定事件, 处理响应结果
xhr.onreadystatechange = function () {
//判断
if (xhr.readyState === 4) {
//判断响应状态码 200-299
if (xhr.status >= 200 && xhr.status < 300) {
//表示成功
resolve(xhr.response);
} else {
//如果失败
reject(xhr.status);
}
}
}
})
//指定回调
p.then(function(value){
console.log(value);
}, function(reason){
console.error(reason);
});
4、Promise的then方法
//创建 promise 对象
const p = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('用户数据');
// reject('出错啦');
}, 1000)
});
(1) 非 promise 类型的属性中使用
const result = p.then(value => {
console.log(value);
return 'iloveyou';
}, reason=>{
console.warn(reason);
});
(2) promise 类型的属性中使用
const result = p.then(value => {
console.log(value);
return new Promise((resolve, reject)=>{
resolve('ok');
reject('error');
});
// 抛出错误
// throw new Error('出错啦!');
throw '出错啦!';
}, reason=>{
console.warn(reason);
});
(3)链式调用
//链式调用
p.then(value=>{
}).then(value=>{
});
5、catch方法
const p = new Promise((resolve, reject)=>{
setTimeout(()=>{
//设置 p 对象的状态为失败, 并设置失败的值
reject("出错啦!");
}, 1000)
});
//方法一
// p.then(function(value){}, function(reason){
// console.error(reason);
// });
//方法二
p.catch(function(reason){
console.warn(reason);
});
练习(解决回调地狱)
//基础用法
const fs = require("fs");
fs.readFile('./resources/为学.md', (err, data1)=>{
fs.readFile('./resources/插秧诗.md', (err, data2)=>{
fs.readFile('./resources/观书有感.md', (err, data3)=>{
let result = data1 + '\r\n' +data2 +'\r\n'+ data3;
console.log(result);
});
});
});
//使用promise
//使用 promise 实现
const fs = require("fs");
const p = new Promise((resolve, reject) => {
fs.readFile("./resources/为学.md", (err, data) => {
resolve(data);
});
});
p.then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./resources/插秧诗.md", (err, data) => {
resolve([value, data]);
});
});
}).then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./resources/观书有感.md", (err, data) => {
//压入
value.push(data);
resolve(value);
});
})
}).then(value => {
console.log(value.join('\r\n'));
});
链式调用
Promise 的一个强大特性是它们支持链式调用。这意味着你可以在一个 .then() 方法的回调函数中返回另一个 Promise,然后继续在链上调用 .then()、.catch() 和 .finally()。
someAsyncFunction()
.then(result => {
// 处理结果
return anotherAsyncFunction(result); // 返回另一个Promise
})
.then(newResult => {
// 处理新的结果
})
.catch(error => {
// 处理错误
})
.finally(() => {
// 清理工作
});
静态方法
Promise 还提供了几个静态方法,如 Promise.all()、Promise.race() 和 Promise.resolve() 等,它们提供了处理多个 Promise 的能力或创建已解决/已拒绝的 Promise 的能力。
注意事项
- 避免在
.then()、.catch()或.finally()的回调函数中创建新的Promise时使用new Promise()构造函数,除非绝对必要。通常,你可以通过返回另一个异步操作的结果来保持链的连续性。 - 总是处理
Promise的错误,即使你确信操作不会失败。使用.catch()或在链的末尾添加一个空的.catch()来捕获可能未被处理的错误。 - 考虑到
async/await语法,它是基于Promise的,可以使得异步代码看起来和同步代码一样。如果可能的话,使用async/await来简化你的异步代码。