Promise
Promise是ES6引入的一种处理异步操作的方式,它提供了一种更优雅、更灵活的方式来处理异步代码,避免了传统回调函数嵌套导致的"回调地狱"问题。Promise已经成为现代JavaScript中处理异步操作的标准方式,也是面试中经常被问到的话题。
Promise的基本概念
什么是Promise?
Promise是一个对象,它代表了一个异步操作的最终完成(或失败)及其结果值。Promise有三种状态:
- pending:初始状态,既不是成功,也不是失败
- fulfilled:操作成功完成
- rejected:操作失败
当Promise的状态从pending变为fulfilled或rejected时,会触发相应的回调函数。一旦Promise的状态发生变化,就不会再改变。
Promise的创建
使用new Promise()构造函数创建一个新的Promise对象,它接受一个函数作为参数,该函数有两个参数:resolve和reject,分别用于将Promise的状态变为fulfilled和rejected。
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve('Operation successful');
} else {
reject('Operation failed');
}
}, 1000);
});
promise
.then(result => {
console.log(result); // 输出: Operation successful
})
.catch(error => {
console.log(error); // 输出: Operation failed
});Promise的方法
then()方法
then()方法用于处理Promise成功时的回调,它接受两个参数:
- onFulfilled:Promise状态变为fulfilled时的回调函数
- onRejected:Promise状态变为rejected时的回调函数(可选)
then()方法返回一个新的Promise对象,因此可以链式调用。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 1000);
});
promise
.then(result => {
console.log(result); // 输出: 10
return result * 2;
})
.then(result => {
console.log(result); // 输出: 20
return result * 3;
})
.then(result => {
console.log(result); // 输出: 60
});catch()方法
catch()方法用于处理Promise失败时的回调,它接受一个参数:
- onRejected:Promise状态变为rejected时的回调函数
catch()方法返回一个新的Promise对象,因此可以链式调用。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Error occurred');
}, 1000);
});
promise
.then(result => {
console.log(result);
})
.catch(error => {
console.log(error); // 输出: Error occurred
});finally()方法
finally()方法用于处理Promise完成(无论成功还是失败)后的回调,它接受一个参数:
- onFinally:Promise完成后的回调函数
finally()方法返回一个新的Promise对象,因此可以链式调用。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Operation successful');
}, 1000);
});
promise
.then(result => {
console.log(result); // 输出: Operation successful
})
.catch(error => {
console.log(error);
})
.finally(() => {
console.log('Operation completed'); // 输出: Operation completed
});Promise.all()方法
Promise.all()方法用于处理多个Promise,它接受一个Promise数组作为参数,返回一个新的Promise对象:
- 当所有Promise都成功时,返回的Promise状态变为fulfilled,其值是所有Promise结果的数组
- 当任一Promise失败时,返回的Promise状态变为rejected,其值是第一个失败的Promise的错误
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 1 successful');
}, 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 2 successful');
}, 2000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 3 successful');
}, 1500);
});
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log(results); // 输出: ['Promise 1 successful', 'Promise 2 successful', 'Promise 3 successful']
})
.catch(error => {
console.log(error);
});Promise.race()方法
Promise.race()方法用于处理多个Promise,它接受一个Promise数组作为参数,返回一个新的Promise对象:
- 当任一Promise完成(成功或失败)时,返回的Promise状态变为与该Promise相同的状态,其值是该Promise的结果
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 1 successful');
}, 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 2 successful');
}, 2000);
});
Promise.race([promise1, promise2])
.then(result => {
console.log(result); // 输出: Promise 1 successful
})
.catch(error => {
console.log(error);
});Promise.allSettled()方法
Promise.allSettled()方法用于处理多个Promise,它接受一个Promise数组作为参数,返回一个新的Promise对象:
- 当所有Promise都完成(成功或失败)时,返回的Promise状态变为fulfilled,其值是一个数组,每个元素包含对应Promise的状态和结果
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 1 successful');
}, 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 2 failed');
}, 2000);
});
Promise.allSettled([promise1, promise2])
.then(results => {
console.log(results);
// 输出:
// [
// { status: 'fulfilled', value: 'Promise 1 successful' },
// { status: 'rejected', reason: 'Promise 2 failed' }
// ]
});Promise.resolve()方法
Promise.resolve()方法返回一个状态为fulfilled的Promise对象,其值是传入的参数。
const promise = Promise.resolve('Operation successful');
promise.then(result => {
console.log(result); // 输出: Operation successful
});Promise.reject()方法
Promise.reject()方法返回一个状态为rejected的Promise对象,其值是传入的参数。
const promise = Promise.reject('Operation failed');
promise.catch(error => {
console.log(error); // 输出: Operation failed
});Promise的应用场景
1. 处理异步操作
Promise最基本的应用场景是处理异步操作,如网络请求、文件读写等:
// 网络请求
function fetchData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
resolve(data);
})
.catch(error => {
reject(error);
});
});
}
fetchData('https://api.example.com/data')
.then(data => {
console.log(data);
})
.catch(error => {
console.log(error);
});
// 文件读写
const fs = require('fs');
function readFile(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, 'utf8', (error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
readFile('example.txt')
.then(data => {
console.log(data);
})
.catch(error => {
console.log(error);
});2. 链式调用
Promise的链式调用使得异步代码更加清晰易读,避免了回调地狱:
// 传统回调地狱
function fetchData(callback) {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
fetch(`https://api.example.com/users/${data.userId}`)
.then(response => response.json())
.then(user => {
fetch(`https://api.example.com/posts?userId=${user.id}`)
.then(response => response.json())
.then(posts => {
callback(posts);
});
});
});
}
// Promise链式调用
function fetchData() {
return fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
return fetch(`https://api.example.com/users/${data.userId}`);
})
.then(response => response.json())
.then(user => {
return fetch(`https://api.example.com/posts?userId=${user.id}`);
})
.then(response => response.json());
}
fetchData()
.then(posts => {
console.log(posts);
})
.catch(error => {
console.log(error);
});3. 并行操作
使用Promise.all()方法可以并行执行多个异步操作,提高效率:
function fetchUser(id) {
return fetch(`https://api.example.com/users/${id}`)
.then(response => response.json());
}
function fetchPosts(userId) {
return fetch(`https://api.example.com/posts?userId=${userId}`)
.then(response => response.json());
}
function fetchComments(postId) {
return fetch(`https://api.example.com/comments?postId=${postId}`)
.then(response => response.json());
}
const userId = 1;
Promise.all([
fetchUser(userId),
fetchPosts(userId),
fetchComments(1)
])
.then(([user, posts, comments]) => {
console.log('User:', user);
console.log('Posts:', posts);
console.log('Comments:', comments);
})
.catch(error => {
console.log(error);
});Promise的优缺点
优点
- 避免回调地狱:Promise的链式调用使得异步代码更加清晰易读
- 更好的错误处理:使用
catch()方法集中处理错误 - 更好的代码组织:Promise使得异步代码的结构更加清晰
- 更好的组合性:使用
Promise.all()、Promise.race()等方法可以组合多个异步操作
缺点
- 浏览器兼容性:虽然现代浏览器都支持Promise,但旧版本浏览器可能需要使用polyfill
- 内存占用:Promise链可能会导致内存占用增加
- 调试困难:Promise链的错误堆栈可能不够清晰
- 无法取消:一旦创建了Promise,就无法取消它
面试常见问题
1. 什么是Promise?Promise有哪些状态?
- Promise的定义:Promise是一个对象,它代表了一个异步操作的最终完成(或失败)及其结果值
- Promise的状态:
- pending:初始状态,既不是成功,也不是失败
- fulfilled:操作成功完成
- rejected:操作失败
2. Promise的链式调用是如何工作的?
then()方法返回一个新的Promise对象- 每个
then()方法的回调函数可以返回一个值或另一个Promise - 如果返回的是一个值,下一个
then()方法的回调函数会接收这个值 - 如果返回的是一个Promise,下一个
then()方法的回调函数会等待这个Promise完成,然后接收它的结果
3. Promise.all()和Promise.race()的区别是什么?
- Promise.all():当所有Promise都成功时,返回的Promise状态变为fulfilled,其值是所有Promise结果的数组;当任一Promise失败时,返回的Promise状态变为rejected,其值是第一个失败的Promise的错误
- Promise.race():当任一Promise完成(成功或失败)时,返回的Promise状态变为与该Promise相同的状态,其值是该Promise的结果
4. 如何实现Promise.all()方法?
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
reject(new TypeError('Argument must be an array'));
}
const results = [];
let completedCount = 0;
const totalCount = promises.length;
if (totalCount === 0) {
resolve(results);
return;
}
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(value => {
results[index] = value;
completedCount++;
if (completedCount === totalCount) {
resolve(results);
}
})
.catch(error => {
reject(error);
});
});
});
}5. Promise和async/await的关系是什么?
- async/await是基于Promise的语法糖,它使得异步代码看起来更像同步代码
- async函数返回一个Promise对象
- await表达式会暂停async函数的执行,等待Promise完成,然后返回Promise的结果
- await只能在async函数中使用
总结
Promise是ES6引入的一种处理异步操作的方式,它提供了一种更优雅、更灵活的方式来处理异步代码,避免了传统回调函数嵌套导致的"回调地狱"问题。Promise有三种状态:pending、fulfilled和rejected。当Promise的状态发生变化时,会触发相应的回调函数。Promise提供了一系列方法,如then()、catch()、finally()、Promise.all()、Promise.race()等,使得我们可以更方便地处理异步操作。Promise已经成为现代JavaScript中处理异步操作的标准方式,它在网络请求、文件读写、异步流程控制等场景下非常实用。