type
Post
status
Published
date
Aug 14, 2021
slug
epmi4g
summary
category
学习思考
tags
并发
创建时间
Apr 7, 2023 07:15 PM
更新时间
Apr 10, 2023 05:46 AM
password
icon
Task List
前言
在优化小程序发布平台的时候,当时研究代码的时候,发现同事写的一段代码,是用来做异步任务切割的,当时很好奇这个任务切割可以解决什么问题。
调研
由于微信那边的限制,一次性只能同时发起 6 个接口请求,如果发起的更多就会报错。所以才有了这个任务切割的代码,但是在我实际的测试过程中发现,同时发起的 6 个请求,其实还是串行执行的,上一个结束之后才会调用下一个,然后我就对比了各个方式的调用结果。
测试
一次性发送 100 个请求
const post = () => { return new Promise((resolve, reject) => { fetch('http://localhost:18740/users/1').then((res) => res.json()).then((r) => { console.log(r); resolve(r) }) }) } for (let index = 0; index < 100; index++) { post() }

await 串行调用 100 个接口
const post = () => { return new Promise((resolve, reject) => { fetch('http://localhost:18740/users/1').then((res) => res.json()).then((r) => { console.log(r); resolve(r) }) }) } for (let index = 0; index < 100; index++) { await post() }

分组调用,等待上一组接口全部返回再调用下一组
const flatJobs = (jobs, concurrent_count) => { const concurrentCount = concurrent_count || 6 return jobs .reduce((queues, c, i) => { if (i % concurrentCount > 0) { queues[queues.length - 1].push(c) return queues } queues.push([c]) return queues }, [[]]) } const runSerialJobsQueue = async (jobs) => { let p = 0 const res = [] while (p < jobs.length) { const part_res = await Promise.all(jobs[p].map(fn => fn().catch(() => false))) res.push(...part_res) ++p } return res } const post = () => { return new Promise((resolve, reject) => { fetch("http://localhost:18740/users/1").then((res) =>res.json()).then(r => resolve(r)) }) } const rows = Array(100).fill(0) const jobs = rows.map((row) => { return async () => { const res = await post(row) return { res } } }) const j = flatJobs(jobs) runSerialJobsQueue(j)

连接程池调用,只要有连接调用结束释放连接,便开始新的连接
async function asyncPool(poolLimit, array, iteratorFn) { // if (shouldAssert) { // assertType(poolLimit, "poolLimit", ["number"]); // assertType(array, "array", ["array"]); // assertType(iteratorFn, "iteratorFn", ["function"]); // } const ret = []; const executing = []; for (const item of array) { const p = Promise.resolve().then(() => iteratorFn(item, array)); ret.push(p); if (poolLimit <= array.length) { const e = p.then(() => executing.splice(executing.indexOf(e), 1)); executing.push(e); if (executing.length >= poolLimit) { await Promise.race(executing); } } } return Promise.all(ret); } const post = () => { return new Promise((resolve, reject) => { fetch('http://localhost:18740/users/1').then((res) => res.json()).then((r) => { console.log(r); resolve(r) }) }) } const results = asyncPool(6, Array(100).fill(0), post);

思考
可以看出,无论是先循环中一次性跑完异步请求,还是 await 串行去跑接口,或者说分组用 promise.all 去执行,或者用线程池去执行,100 个接口的用时都几乎没有差别。那就有了新的疑问,既然还是一个个处理的,为什么超过 6 个,微信就给限制了,反正也是一个个处理的。
经过分析,其实这 100 个接口虽然是同时发起,也就是并发的,但是却不是同时执行(并行)的,微信既限制了并发量,也限制了并行量,所以才导致了这样的结果。
当然,微信去限制并发和并行是为了安全考虑的,一次性发送太多接口,会导致系统炸了!