一小池勺❤️❤️❤️ ❤️❤️❤️❤️胸有惊雷而面如平湖者,可拜上将军也。
停止在 JavaScript 中使用 Promise.all()
JavaScript 中的 Promises 是什么?
从本质上讲,Promise 对象表示异步操作的最终完成或失败。有趣的是,当 promise 被创建时,其值可能不会立即可用。
const promise = new Promise((resolve, reject) => { // 进行一些异步操作 ...... if(/* 该操作成功 */) { resolve(result) } else { reject(error) } })
Promise对象有3种状态:
- Pending(待定):这是初始状态,既没有实现也没有被拒绝。
- Fulfilled(已实现):当 promise 成功完成并产生值时的状态。
- Rejected(已拒绝):当发生错误并且 promise 中的操作不成功时的状态。
一旦 promise 被解决,你可以使用 .then() 来处理结果,使用 .catch() 来管理其执行过程中出现的任何错误。
promise .then(result => { console.log("成功的结果是:", result); }) .catch(error => { console.log("错误的原因是:"error); })
理解 Promise.all()
当同时处理多个 promises
时,你可以利用内置的 Promise.all([])
方法。此方法接受一个 promises
数组并返回一个统一的 promise
。关键是,只有当所有输入的 promises
都成功解决时,这个组合的 promise
才会解决。如果其中一个失败,整个 promise
就会被拒绝。以下是一个例子:
const promise1 = fetch('https://api.example.com/data1') const promise2 = fetch('https://api.example.com/data2') const promise3 = fetch('https://api.example.com/data3') Promise.all([promise1, promise2, promise3]) .then(values => { console.log('所有数据已获取成功'); }) .catch(error => { console.log("发生了错误", error); })
这种方法通常用于多个相关异步任务且其工作相互依赖的情况,因此我们希望在继续执行代码之前,所有异步任务都能成功。
揭示 Promise.allSettled()
使用 Promise.allSettled([])
与 Promise.all([])
类似,但不同之处在于它会等待所有输入的 promises
完成或被拒绝,并返回描述每个 promise 结果的对象数组。
const promise1 = Promise.resolve('成功 1') const promise2 = Promise.reject('错误 2') const promise3 = Promise.resolve('成功 3') Promise.allSettled([promise1, promise2, promise3]) .then(results => { results.forEach(result => { if(result.status === 'fulfilled') { console.log('成功取得值') } else { console.log('被拒绝,原因如下:', result.reason) } }) })
------控制台输出------ // 输出 // 成功取得值:成功 1 // 因为原因被拒绝:错误 2 // 成功取得值:成功 3
它通常用于处理不互相依赖的异步操作,你想知道每一个的结果。
为什么 Promise.allSettled() 更出色
总的来说,使用 Promise.allSettled() 而不是 Promise.all() 在大多数常见情况下都有其优势:
全面的结果信息
如果 promises 中的任何一个被拒绝,Promise.all() 的立即拒绝可能会使得确定其他 promises 的状态变得困难,尤其是当其他 promises 成功解决时。使用 Promise.allSettled([]) 可以为你提供结果的完整画面。
优雅的错误处理
Promise.all() 的“快速失败”方法在你想继续进行,而其中一个失败时可能会受到限制,而 Promise.allSettled() 允许你单独处理每个 promise 的结果。
批量操作
当处理批量操作时,其中的单个操作是独立的,你可能不希望整个批次因操作失败而失败。
明智的决策
使用 Promise.allSettled() 后,你可以在获得所有 promises 的结果后做出更明智的决策。例如,当你从不同的 API 获取数据,其中一个失败时,你可以决定是否继续处理数据或提供带有错误消息的通知。
增强的用户体验
通常,为用户提供必要的部分结果和错误通知要比使用某些通用消息使整个操作失败更好。Promise.allSettled() 使这种方法易于实施。
展示 Promise.allSettled() 和 Promise.all() 的不同之处:
场景一:数据同步和错误处理
假设我们数据同步任务,需要从多个外部数据源获取数据并进行处理。在这种情况下,使用 Promise.allSettled() 可以提供更全面的结果信息和优雅的错误处理。
function fetchDataFromSource1() { // 模拟从数据源1获取数据 return new Promise((resolve, reject) => { setTimeout(() => { // 假设数据成功返回 resolve('Data from Source 1'); // 假设数据获取失败 // reject('Error fetching data from Source 1'); }, 1000); }); } function fetchDataFromSource2() { // 模拟从数据源2获取数据 return new Promise((resolve, reject) => { setTimeout(() => { // 假设数据成功返回 resolve('Data from Source 2'); // 假设数据获取失败 // reject('Error fetching data from Source 2'); }, 2000); }); } function fetchDataFromSource3() { // 模拟从数据源3获取数据 return new Promise((resolve, reject) => { setTimeout(() => { // 假设数据成功返回 resolve('Data from Source 3'); // 假设数据获取失败 // reject('Error fetching data from Source 3'); }, 1500); }); } function processSuccessfulData(data) { console.log('Processing successful data:', data); // 进行数据处理的逻辑 } function handleError(error) { console.log('Handling error:', error); // 处理错误的逻辑 } function handleOverallError(error) { console.log('Handling overall error:', error); // 处理整体错误的逻辑 } const dataSources = [ fetchDataFromSource1(), fetchDataFromSource2(), fetchDataFromSource3() ]; Promise.allSettled(dataSources) .then(results => { results.forEach(result => { if (result.status === 'fulfilled') { processSuccessfulData(result.value); } else { handleError(result.reason); } }); }) .catch(error => { handleOverallError(error); });
我们假设有三个数据源:Source 1、Source 2 和 Source 3。每个数据源都是一个返回 Promise 的函数,模拟了从外部数据源获取数据的过程。我们通过 setTimeout 来模拟异步操作。
在主函数中,我们创建一个包含三个数据源的数组 dataSources。然后,使用 Promise.allSettled(dataSources) 并行获取数据,并遍历结果数组 results。对于每个结果,如果状态为 ‘fulfilled’,表示成功获取数据,我们调用 processSuccessfulData() 函数进行处理,将获取到的数据进行业务的操作;如果状态为 ‘rejected’,表示获取数据失败,我们调用 handleError() 函数处理错误。同时,如果整体的 Promise.allSettled() 链路出现错误,我们通过 catch() 捕获错误并调用 handleOverallError() 函数处理。
场景二:依赖关系和快速失败
假设需要依次执行多个操作,如果其中一个操作失败,则停止执行剩余操作。在这种情况下,使用 Promise.all() 可以实现快速失败和批量操作。
function performTask1() { // 模拟执行任务1 return new Promise((resolve, reject) => { setTimeout(() => { // 假设任务1成功 resolve('Task 1 completed'); // 假设任务1失败 // reject('Error executing Task 1'); }, 1000); }); } function performTask2() { // 模拟执行任务2 return new Promise((resolve, reject) => { setTimeout(() => { // 假设任务2成功 resolve('Task 2 completed'); // 假设任务2失败 // reject('Error executing Task 2'); }, 2000); }); } function performTask3() { // 模拟执行任务3 return new Promise((resolve, reject) => { setTimeout(() => { // 假设任务3成功 resolve('Task 3 completed'); // 假设任务3失败 // reject('Error executing Task 3'); }, 1500); }); } function processResults(results) { console.log('Processing results:', results); // 处理所有任务成功的结果的逻辑 } function handleError(error) { console.log('Handling error:', error); // 处理任务失败的错误的逻辑 } const tasks = [ performTask1(), performTask2(), performTask3() ]; Promise.all(tasks) .then(results => { processResults(results); }) .catch(error => { handleError(error); });
我们假设有三个任务:Task 1、Task 2 和 Task 3。每个任务都是一个返回 Promise 的函数,模拟了执行任务的过程。
在主函数中,我们创建一个包含三个任务的数组 tasks。然后,使用 Promise.all(tasks) 按顺序执行任务,并使用 then() 处理所有任务成功的结果。如果其中任何一个任务被拒绝,catch() 将捕获错误并调用 handleError() 函数处理错误。
以上两个场景展示了 Promise.allSettled() 和 Promise.all() 在不同场景下的应用。Promise.allSettled() 适用于处理多个独立的异步操作,并提供完整的结果信息和灵活的错误处理;而 Promise.all() 更适用于按特定顺序执行任务,并在任何一个任务失败时快速终止并处理错误。
总结
简而言之,Promise.allSettled() 比 Promise.all() 更加灵活和强大,它可以提供完整的结果信息、优雅的错误处理、批量操作、明智的决策和增强的用户体验等优势。而在某些情况下,Promise.all() 也是很有价值的,具体使用哪种方法应该根据具体情况而定。