DrillLab
第 44 / 105 道44 / 105 · #311

怎么处理异步操作

Handle asynchronous operations

先自己答,再往下看Answer it yourself first

一句话(按历史讲最清楚):回调 → Promise → async/await, 外加事件和 for await…of处理流式数据。

但这题真正的考点是 Promise 的四个静态方法怎么选, 一定会追问:

方法什么时候 resolve用在哪
all全部成功才成功,一个失败立刻失败几个都必须成功(页面必需的多个接口)
allSettled全部结束就成功, 不管成败批量操作,要知道每一个的结果 (批量上传,报告哪几个失败了)
race第一个结束的说话, 成功失败都算超时控制
any第一个成功的, 全失败才失败多个镜像源取最快能用的那个

all 的坑: 它是 fail-fast 的 —— 一个失败,其他已经在飞的请求不会被取消, 而且你拿不到其他的结果。 「批量操作要逐个报告」的场景该用allSettled

会追问:「怎么给一个请求加超时?」——Promise.race配一个定时 reject 的 Promise; 更好的是用 AbortController, 因为它能真的把请求掐掉race 只是不等了。 这两个的区别在我们那道 fetch 变式题里也讲过。

In one line, and history tells it best: callbacks → Promises → async/await, plus events and for await…of for streaming data.

But what this question is really after is how you pick among the four static Promise methods, and they will ask:

MethodWhen it resolvesWhere you use it
allSucceeds only if all of them succeed, and fails the instant one failsEverything must succeed (several calls the page needs)
allSettledSucceeds once everything has finished, win or loseBulk work where you need each result (a batch upload, reporting which ones failed)
raceWhoever finishes first speaks, success or failureTimeouts
anyThe first success; it fails only if all failSeveral mirrors, take the fastest one that works

The trap in all: it is fail-fast — one rejection and the other requests already in flight are not cancelled, and you never see their results. When bulk work has to report item by item, reach for allSettled.

Follow-up: “How do you put a timeout on a request?” — Promise.race against a Promise that rejects on a timer. Better is AbortController, because it actually kills the request, whereas race merely stops waiting. We walk through that difference in the fetch variant question too.

JavaScript四个方法与超时The four methods, and timeouts示意Illustrative
1// 都必须成功
2const [user, posts] = await Promise.all([getUser(), getPosts()]);
3
4// 要逐个知道结果
5const results = await Promise.allSettled(files.map(upload));
6const failed = results.filter((r) => r.status === "rejected");
7
8// 超时:race 只是「不等了」,请求还在飞
9const withTimeout = (p, ms) =>
10 Promise.race([
11 p,
12 new Promise((_, rej) => setTimeout(() => rej(new Error("超时")), ms)),
13 ]);
14
15// 更好:AbortController 真的掐掉请求
16const c = new AbortController();
17setTimeout(() => c.abort(), 5000);
18await fetch(url, { signal: c.signal });
1// All of them have to succeed
2const [user, posts] = await Promise.all([getUser(), getPosts()]);
3
4// You need the result of each one
5const results = await Promise.allSettled(files.map(upload));
6const failed = results.filter((r) => r.status === "rejected");
7
8// Timeout: race only means "stop waiting"; the request is still in flight
9const withTimeout = (p, ms) =>
10 Promise.race([
11 p,
12 new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), ms)),
13 ]);
14
15// Better: AbortController really does cancel the request
16const c = new AbortController();
17setTimeout(() => c.abort(), 5000);
18await fetch(url, { signal: c.signal });