为什么我们需要Promise(一)
前言
Promise 是 JavaScript 中的一个原生对象,用于处理异步操作,它提供了一种更优雅的方式来组织和管理异步代码,避免了回调地狱(callback hell),使得异步逻辑更加清晰和易于维护。Promise 代表一个尚未完成(或已经完成)的异步操作的结果,最终这个结果会变为 resolved(成功)或 rejected(失败)。那为什么我们需要Promise这个对象呢?
正文
众所周知Promise 有三种状态,分别是 pending(等待中)、fulfilled(已成功)和 rejected(已失败)。状态一旦从 pending 变为 fulfilled 或 rejected,就不可再变。通过 new Promise((resolve, reject) => {})
来创建一个 Promise 实例。resolve
函数用来改变 Promise 的状态为 fulfilled,reject
函数用来改变状态为 rejected。同时Promise 支持链式调用,即 .then()
和 .catch()
方法,用于注册 fulfilled 和 rejected 状态的回调函数,并返回一个promise对象。
接下来我们聊聊常见的一些promise使用:
解决回调地狱
在没有Promise之前,复杂的异步操作会导致多层嵌套的回调函数,这不仅使得代码难以阅读和维护,还可能引发“回调地狱”。Promise通过链式调用的方式,使得异步逻辑更加扁平和有序。
函数回调地狱通常发生在需要进行多个连续的异步操作时,每个操作都需要等待前一个操作完成才能开始,这导致代码中充满了层层嵌套的回调函数,不仅难以阅读和维护,而且容易出错。
const data = 'data';
setTimeout(() => {
console.log('1');
if (data) {
setTimeout(() => {
console.log('2');
if (data) {
setTimeout(() => {
console.log('3');
console.log(data);
}, 1000);
}
}, 1000);
}
}, 1000);
而使用Promise重写这段代码可以让异步逻辑更加清晰和易于管理。
const data = 'data';
function delay(ms, value) {
return new Promise(resolve => setTimeout(() => resolve(value), ms));
}
delay(1000, data)
.then(data => {
console.log('1');
if (data) {
return delay(1000);
}
})
.then(() => {
console.log('2');
// 由于data始终为真,这里直接进入下一个延迟
return delay(1000);
})
.then(() => {
console.log('3');
console.log(data);
})
.catch(error => {
console.error('发生错误:', error);
});
更好的错误处理
Promise提供了统一的错误处理机制,即通过.catch
方法可以捕获前面Promise链中的任何错误,避免了在每个回调中都需要单独处理错误的情况,使得错误处理更加集中和清晰。
const p=new Promise((resolve, reject) => {
reject('result')
})
p.then((result) =>{
console.log(result)
return 'success';
})
.then((result) => {
console.log(result)
return 'success';
})
.catch((error) => console.log(error))
易于理解和维护
Promise的链式语法和明确的状态(pending、fulfilled、rejected)使得异步代码的流程更容易理解。开发者可以直观地看到一系列异步操作是如何依次执行的,以及它们之间的依赖关系。
function fetchDataFromServer(url) {
return new Promise((resolve, reject) => {
// 模拟从服务器获取数据
setTimeout(() => {
if (url === 'success') {
resolve('数据获取成功');
} else {
reject('数据获取失败');
}
}, 2000);
});
}
function processData(data) {
return new Promise((resolve) => {
// 模拟数据处理过程
setTimeout(() => {
resolve(`处理后的数据: ${data.toUpperCase()}`);
}, 1000);
});
}
function saveProcessedData(processedData) {
return new Promise((resolve) => {
// 模拟保存处理后的数据到数据库
setTimeout(() => {
resolve(`数据已保存: ${processedData}`);
}, 1500);
});
}
// 利用Promise链处理异步操作
fetchDataFromServer('success') // 假设请求成功
.then(data => {
console.log(data);
return processData(data); // 数据获取成功后进行处理
})
.then(processedData => {
console.log(processedData);
return saveProcessedData(processedData); // 数据处理完成后保存
})
.then(savedData => {
console.log(savedData);
console.log('所有操作完成');
})
.catch(error => {
console.error('操作过程中出现错误:', error);
});
支持并发和串行操作
Promise.all 允许并行执行多个Promise,并在所有Promise都成功完成后得到结果;Promise.race 可以在第一个完成的Promise(无论成功还是失败)后立即得到通知。这为管理异步任务提供了强大的工具。
Promise.all 示例
假设我们有一个场景,需要同时从两个API获取数据,只有当两个请求都成功时,我们才处理这些数据。
function fetchDataFromAPI1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Data from API 1");
}, 2000);
});
}
function fetchDataFromAPI2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Data from API 2");
}, 1000);
});
}
Promise.all([fetchDataFromAPI1(), fetchDataFromAPI2()])
.then(results => {
console.log("Both requests succeeded:", results);
})
.catch(error => {
console.error("At least one request failed:", error);
});
Promise.all
等待两个API调用都成功完成,然后一起打印出结果。如果任何一个请求失败,.catch
将捕获错误。
Promise.race 示例
现在考虑一个场景,我们想尽快从两个数据源获取信息,不论哪个数据源先返回数据我们就使用那个。
function getDataFastestWay() {
const fastSourcePromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Data_1 already in progress");
}, 500);
});
const slowSourcePromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Data_2 already in progress");
}, 3000);
});
return Promise.race([fastSourcePromise, slowSourcePromise]);
}
getDataFastestWay().then(result => {
console.log(result);
});
得到结果为:
当我们调换延迟时间:得到结果:
Promise.race
将在第一个完成的Promise(这里是快速数据源,在500毫秒后完成)之后立即解决,输出 较快的数据而忽略较慢的数据源响应。这样可以确保我们能够尽快得到可用的结果。
转载自:https://juejin.cn/post/7384636970034135090