在 Javascript 中使用异步函数的更好方法
如果你从事 Web 开发,那么你 100% 可能至少用过一些异步函数。使用异步函数的方法有很多种,例如 `async` 和 `async.get()`。.then()但async/await.如果我告诉你,还有更好的方法可以使用异步函数,将请求时间缩短一半呢?🤯
没错,是真的!JavaScript 运行时提供了许多我们通常不了解或不使用的功能。其中之一就是 Promise 类的静态方法。
在这篇简短的博文中,我们将探讨如何使用这些静态方法来改进我们的异步函数调用。
Promise.all()
该Promise.all()方法接受一个 Promise 可迭代对象作为输入,并返回一个解析为输入 Promise 结果数组的单个 Promise。如果任何输入 Promise 被拒绝,或者非 Promise 对象抛出错误,则该方法立即拒绝,并使用第一个拒绝方法。
以下是一个例子:
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve,reject)=>{
setTimeout(resolve,100,'foo');
})
Promise.all([promise1,promise2,promise3]).then((values)=>{
console.log(values);
})
// expected output Array [3,42,'foo']
现在让我们看看如何利用它来加速异步调用:
顺序执行与并发执行
通常情况下,当异步函数调用一个接一个地进行时,每个请求都会被前面的请求阻塞,这种模式也称为“瀑布式”模式,因为每个请求只有在前一个请求返回一些数据后才能开始。
顺序执行模式。
// Simulate two API calls with different response times
function fetchFastData() {
return new Promise(resolve => {
setTimeout(() => {
resolve("Fast data");
}, 2000);
});
}
function fetchSlowData() {
return new Promise(resolve => {
setTimeout(() => {
resolve("Slow data");
}, 3000);
});
}
// Function to demonstrate sequential execution
async function fetchDataSequentially() {
console.log("Starting to fetch data...");
const startTime = Date.now();
// Start both fetches concurrently
const fastData = await fetchFastData();
const slowData = await fetchSlowData();
const endTime = Date.now();
const totalTime = endTime - startTime;
console.log(`Fast data: ${fastData}`);
console.log(`Slow data: ${slowData}`);
console.log(`Total time taken: ${totalTime}ms`);
}
fetchDataSequentially()
/*
expected output:
Starting to fetch data...
Fast data: Fast data
Slow data: Slow data
Total time taken: 5007ms
*/
我们可以一次性发出所有请求,然后等待它们全部完成。这样Promise.all(),由于请求无需等待前一个请求完成,它们可以提前启动,从而提前得到解决。Promise.all()当所有传递给它的 Promise 都得到解决后,它会返回一个包含已解决 Promise 的数组。以下是如何使用 Promise
改进我们的函数。fetchData
并发执行模式
async function fetchDataConcurrently() {
console.log("Starting to fetch data...");
const startTime = Date.now();
// Start both fetches concurrently
const fastDataPromise = fetchFastData();
const slowDataPromise = fetchSlowData();
// Wait for both promises to resolve
const [fastData, slowData] = await Promise.all([fastDataPromise, slowDataPromise]);
const endTime = Date.now();
const totalTime = endTime - startTime;
console.log(`Fast data: ${fastData}`);
console.log(`Slow data: ${slowData}`);
console.log(`Total time taken: ${totalTime}ms`);
}
/*
expected output:
Starting to fetch data...
Fast data: Fast data
Slow data: Slow data
Total time taken: 3007ms
*/
为了更直观地展示,这里提供了一个示意图。
我们将所有 Promise 对象放在一个数组中,传递给 `Promise.all()` 方法,然后等待它完成。如果有多个请求,这种方法可以节省大量时间。
不过,有一点需要考虑:如果某个 Promise 被拒绝了怎么办?如果我们Promise.all()在这种情况下使用 `reject`,它只会拒绝被拒绝的 Promise。如果我们想要获取所有 Promise 的结果,无论它们是已解决还是已拒绝,该怎么办?
为了处理这种情况,我们可以使用 `reject` Promise.allSettled()。让我们也来了解一下它。
Promise.allSettled()
它是 的一个略微变体Promise.all(),区别在于Promise.allSettled()它总是会解决,无论传递给它的 promise 是解决还是拒绝,它都会返回包含传递给它的 promise 的结果的数组。
例子:
const promise1 = Promise.reject("failure");
const promise2 = 42;
const promise3 = new Promise((resolve) => {
setTimeout(resolve, 100, 'foo');
});
Promise.allSettled([promise1, promise2, promise3]).then((results) => {
console.log(results);
});
// expected output: Array [
// { status: "rejected", reason: "failure" },
// { status: "fulfilled", value: 42 },
// { status: "fulfilled", value: 'foo' }
// ]
另一个有用的静态方法是 ` Promise.race()timeout`,它可以用来为异步函数实现超时。让我们看看它是如何工作的:
Promise.race()
Promise.race() 方法返回一个 Promise,当数组中传递给它的任何一个 Promise 被满足或拒绝时,该 Promise 就会被满足或拒绝,并返回一个值或拒绝原因。
例子
const promise1 = new Promise((resolve,reject)=>{
setTimeout(resolve,500,'one');
})
const promise2 = new Promise((resolve,reject)=>{
setTimeout(resolve,100,'two');
})
Promise.race([promise1,promise2]).then((value)=>{
console.log(value);
// Both resolves but promise2 is faster.
})
// expected output: 'two'
让我们看看如何利用它来实现超时机制:
function fetchData() {
return new Promise(resolve => {
setTimeout(() => {
resolve("Fast data");
}, 6000);
});
}
const fetchDataPromise = fetchData();
function fetchDataWithTimeout(promise, duration) {
let timeoutId;
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => {
reject(new Error(`Operation timed out after ${duration} ms`));
}, duration);
});
return Promise.race([
promise,
timeoutPromise
]).finally(()=>{
/*
As pointed out by @vicariousv , if we do not clear the timeout, then
even if our promise resolves before the timeout, the set timeout will
still keep running. The process will end only after the timeout
completes, which is something to consider when using serverless
functionsas we pay for the runtime.
*/
clearTimeout(timeoutId);
})
}
fetchDataWithTimeout(fetchDataPromise,5000).then((result)=>{
console.log(result)
}).catch((error)=>{
console.log(error)
})
/*
expected result:
Too late
*/
这篇博文就到这里了。如果你想了解更多关于异步 Promise 的内容,请查看我的另一篇关于异步 Promise 的博文:《异步 JavaScript:你永远记住的精简版》。
感谢阅读,希望您有所收获!
文章来源:https://dev.to/adityabhattad/better-way-to-make-async-calls-calls-in-javascript-dag

