各位观众,晚上好!我是今晚的主讲人,咱们今天来聊聊 JavaScript 里一个非常有意思的函数:Promise.race()
。 保证让大家听得明白,用得溜!
开场白:龟兔赛跑的 Promise 世界
大家小时候都听过龟兔赛跑的故事吧?兔子自恃跑得快,中途睡大觉,结果被乌龟超了。Promise.race()
就有点像这个故事,它让一堆 Promise 赛跑,但它只关心谁第一个跑完,至于其他的,它根本不在乎!
什么是 Promise.race()
?
Promise.race()
是 JavaScript 中 Promise
对象的一个静态方法。它的作用是:
- 接收一个包含多个
Promise
对象的数组(或者任何可迭代对象)。 - 并发地执行这些
Promise
。 - 一旦其中一个
Promise
完成(fulfilled 或 rejected),Promise.race()
返回的 Promise 也会立即以相同的结果(value 或 reason)完成。
简单来说:谁跑得最快,就听谁的!
语法:
Promise.race(promises);
promises
: 一个可迭代对象,比如数组,其中包含Promise
对象。
返回值:
- 一个新的
Promise
对象。这个 Promise 的状态和值/原因取决于输入数组中第一个完成的 Promise。
举个栗子:Promise.race()
的初体验
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 1 完成!');
}, 2000); // 2秒后完成
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 2 失败!');
}, 1000); // 1秒后失败
});
Promise.race([promise1, promise2])
.then(result => {
console.log('最终结果:', result); // 输出: 最终结果: Promise 2 失败!
})
.catch(error => {
console.error('出现错误:', error); // 输出: 出现错误: Promise 2 失败!
});
// 注意:这里promise2先reject,所以会直接进入catch
在这个例子中,promise2
比 promise1
先完成(reject)。因此,Promise.race()
返回的 Promise 也会立即以 promise2
的结果(reject)完成。也就是说,promise1
辛辛苦苦跑完,也没人理它,因为 promise2
已经赢了!
Promise.race()
的工作原理:深入解析
-
并发执行:
Promise.race()
会立即并发地执行传入的 Promise 数组中的所有 Promise。这意味着它们会同时开始运行,而不是一个接一个地执行。 -
监听状态变化:
Promise.race()
会监听每个 Promise 的状态变化(fulfilled 或 rejected)。 -
第一个完成者胜出: 一旦其中一个 Promise 完成(无论是 fulfilled 还是 rejected),
Promise.race()
就会立即采用该 Promise 的状态和值/原因。 -
忽略其他 Promise: 在第一个 Promise 完成后,
Promise.race()
会忽略数组中所有其他 Promise 的结果。即使其他 Promise 后来也完成了,Promise.race()
也不会再改变它的状态。
用表格来总结一下:
特性 | 描述 |
---|---|
并发执行 | 所有 Promise 同时开始运行。 |
状态监听 | 监听每个 Promise 的 fulfilled 或 rejected 状态。 |
胜出条件 | 第一个完成(fulfilled 或 rejected)的 Promise 决定了 Promise.race() 的结果。 |
忽略其他 Promise | 一旦有 Promise 胜出,其他 Promise 的结果将被忽略。 |
Promise.race()
的常见应用场景:
Promise.race()
在实际开发中有很多用处,下面列举几个常见的场景:
-
超时控制:
有时候,我们需要对某个操作设置一个超时时间。如果操作在规定时间内没有完成,就认为它失败了。
Promise.race()
可以很方便地实现这个功能。function withTimeout(promise, timeout) { const timeoutPromise = new Promise((resolve, reject) => { setTimeout(() => { reject('操作超时!'); }, timeout); }); return Promise.race([promise, timeoutPromise]); } // 模拟一个需要很长时间才能完成的 Promise const longRunningPromise = new Promise(resolve => { setTimeout(() => { resolve('耗时操作完成!'); }, 3000); }); withTimeout(longRunningPromise, 2000) // 设置 2 秒超时 .then(result => { console.log('结果:', result); }) .catch(error => { console.error('错误:', error); // 输出: 错误: 操作超时! });
在这个例子中,
withTimeout
函数接收一个 Promise 和一个超时时间。它创建一个新的 PromisetimeoutPromise
,在指定的时间后 reject。然后,使用Promise.race()
将原始 Promise 和timeoutPromise
放在一起赛跑。如果原始 Promise 在超时时间内没有完成,timeoutPromise
就会先 reject,从而导致整个操作失败。 -
服务降级:
在高并发的场景下,某些服务可能会出现性能瓶颈。为了保证系统的可用性,我们可以使用
Promise.race()
实现服务降级。async function fetchFromServiceA() { // 尝试从服务 A 获取数据 try { const response = await fetch('https://service-a.example.com/data'); return await response.json(); } catch (error) { console.error('服务 A 失败:', error); return null; } } async function fetchFromServiceB() { // 尝试从服务 B 获取数据(备用服务) try { const response = await fetch('https://service-b.example.com/data'); return await response.json(); } catch (error) { console.error('服务 B 失败:', error); return null; } } async function getData() { const data = await Promise.race([fetchFromServiceA(), fetchFromServiceB()]); if (data) { return data; } else { // 如果两个服务都失败了,返回默认数据或者抛出错误 return { message: '所有服务均不可用,返回默认数据' }; } } getData().then(data => { console.log('获取到的数据:', data); });
在这个例子中,我们首先尝试从服务 A 获取数据。如果服务 A 失败了,就尝试从服务 B 获取数据。
Promise.race()
保证了我们总是能够尽快地得到结果,即使某个服务出现了问题。 -
竞态条件处理:
在某些情况下,多个操作可能会竞争同一个资源。
Promise.race()
可以用来处理这种竞态条件。let resourceLocked = false; async function acquireResource() { return new Promise((resolve, reject) => { if (!resourceLocked) { resourceLocked = true; resolve('成功获取资源!'); } else { reject('资源已被占用!'); } }); } async function operation1() { try { const result = await acquireResource(); console.log('操作 1:', result); // 模拟使用资源 await new Promise(resolve => setTimeout(resolve, 1000)); resourceLocked = false; // 释放资源 } catch (error) { console.error('操作 1:', error); } } async function operation2() { try { const result = await acquireResource(); console.log('操作 2:', result); // 模拟使用资源 await new Promise(resolve => setTimeout(resolve, 500)); resourceLocked = false; // 释放资源 } catch (error) { console.error('操作 2:', error); } } Promise.race([operation1(), operation2()]).then(() => { console.log("其中一个操作已经完成,无论成功与否"); });
在这个例子中,
acquireResource
函数尝试获取一个共享资源。如果资源已经被占用,函数会 reject。operation1
和operation2
都会尝试获取资源。Promise.race()
确保只有一个操作能够成功获取资源,另一个操作会失败。 -
取消操作(高级用法):
虽然
Promise.race()
本身不能直接取消 Promise,但我们可以结合AbortController
来实现取消操作的效果。const controller = new AbortController(); const signal = controller.signal; async function fetchData() { try { const response = await fetch('https://api.example.com/data', { signal }); return await response.json(); } catch (error) { if (error.name === 'AbortError') { console.log('Fetch 操作被取消!'); } else { console.error('Fetch 操作失败:', error); } return null; } } const timeoutPromise = new Promise((resolve, reject) => { setTimeout(() => { controller.abort(); // 取消 fetch 操作 reject('请求超时!'); }, 2000); }); Promise.race([fetchData(), timeoutPromise]) .then(data => { if (data) { console.log('获取到的数据:', data); } }) .catch(error => { console.error('错误:', error); }); // 在 1 秒后取消操作 (模拟用户取消操作) setTimeout(() => { controller.abort(); }, 1000);
在这个例子中,我们使用
AbortController
来控制fetch
操作。timeoutPromise
在 2 秒后会调用controller.abort()
取消fetch
操作。Promise.race()
确保如果fetch
操作超时,就会被取消。
注意事项:
-
空数组: 如果传递给
Promise.race()
的数组为空,它将返回一个永远 pending 的 Promise。Promise.race([]) .then(() => { console.log('永远不会执行到这里'); }) .catch(() => { console.log('永远不会执行到这里'); });
-
非 Promise 值: 如果数组中包含非 Promise 值,这些值会被转换为 Promise。
Promise.race([1, Promise.resolve(2), 'hello']) .then(result => { console.log('结果:', result); // 输出: 结果: 1 });
-
错误处理: 记得使用
.catch()
来处理Promise.race()
返回的 Promise 的 rejection 情况。
Promise.race()
的一些高级技巧:
-
动态添加 Promise: 虽然
Promise.race()
接收的是一个数组,但我们可以利用 JavaScript 的灵活性,动态地向数组中添加 Promise。const promises = []; promises.push(new Promise(resolve => setTimeout(() => resolve('Promise A'), 1500))); promises.push(new Promise(resolve => setTimeout(() => resolve('Promise B'), 1000))); Promise.race(promises) .then(result => console.log('结果:', result)); // 输出: 结果: Promise B // 稍后添加一个更快的 Promise setTimeout(() => { promises.push(new Promise(resolve => setTimeout(() => resolve('Promise C'), 500))); }, 200); // 稍等片刻,确保前两个 Promise 已经开始执行
这个例子展示了如何动态地向
promises
数组中添加 Promise。需要注意的是,如果后续添加的 Promise 完成速度更快,它将胜出。 -
结合
async/await
:Promise.race()
可以与async/await
结合使用,使代码更加简洁易读。async function runRace() { try { const result = await Promise.race([ new Promise(resolve => setTimeout(() => resolve('Promise 1'), 1000)), new Promise(reject => setTimeout(() => reject('Promise 2'), 500)), ]); console.log('结果:', result); } catch (error) { console.error('错误:', error); // 输出: 错误: Promise 2 } } runRace();
在这个例子中,
async/await
使得Promise.race()
的使用更加直观。
总结:
Promise.race()
是一个强大的工具,可以用来解决各种并发问题。通过合理地运用它,我们可以提高程序的性能、可靠性和用户体验。
最后,再用一句话总结: Promise.race()
就像一场比赛,只有最快的选手才能赢得奖牌!希望大家以后在开发中能灵活使用 Promise.race()
,让你的代码跑得更快,更稳定!
今天的讲座就到这里,谢谢大家! 如果大家有什么问题,欢迎提问。