使用可观察量将使该任务更容易推理(而不是使用命令式编程)。
浏览器通常允许您并行发出 6 个请求,并将其他请求排队。但我们不希望浏览器为我们管理该队列(或者如果我们在节点环境中运行,我们就不会拥有该队列)。
我们想要什么:我们想要上传大量文件。应通过始终并行运行 5 个请求来尽可能高效地对它们进行排队和上传。 (因此我们为应用程序中的其他请求免费保留 1 个)。
为了演示这一点,我们首先构建一些模拟:
function randomInteger(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const mockPayrollsService = {
sendFilesPaymentName: (file: File) => {
return of(file).pipe(
// simulate a 500ms to 1.5s network latency from the server
delay(randomInteger(500, 1500))
);
}
};
// array containing 50 files which are mocked
const files: File[] = Array.from({ length: 50 })
.fill(null)
.map(() => new File([], ""));
我认为上面的代码是不言自明的。我们正在生成模拟,以便我们可以看到代码的核心如何实际运行,而无需真正访问您的应用程序。
现在,主要部分:
const NUMBER_OF_PARALLEL_CALLS = 5;
const onFilePaymentSelect = (files: File[]) => {
const uploadQueue$ = from(files).pipe(
map(file => mockPayrollsService.sendFilesPaymentName(file)),
mergeAll(NUMBER_OF_PARALLEL_CALLS)
);
uploadQueue$
.pipe(
scan(nbUploadedFiles => nbUploadedFiles + 1, 0),
tap(nbUploadedFiles =>
console.log(`${nbUploadedFiles}/${files.length} file(s) uploaded`)
),
tap({ complete: () => console.log("All files have been uploaded") })
)
.subscribe();
};
onFilePaymentSelect(files);
- We use
from
将文件逐个发送到可观察对象中
- using
map
,我们准备 1 个文件的请求(但由于我们不订阅它并且可观察到的内容是冷的,因此该请求只是准备好了,而不是触发!)
- 我们现在使用
mergeMap
运行调用池。感谢以下事实:mergeMap
以并发度为参数,我们可以说“请同时运行最多 5 个调用”
- 然后我们使用
scan
仅用于显示目的(计算已成功上传的文件数)
这是一个现场演示:https://stackblitz.com/edit/rxjs-zuwy33?file=index.ts https://stackblitz.com/edit/rxjs-zuwy33?file=index.ts
打开控制台可以看到我们不是一次性上传所有的