Wel*_*lls 2 javascript arrays api timer promise
我有一个函数,它获取 ID 列表,将它们转换为数组 URL,然后使用 map 函数来触发获取请求。它工作得很好,但启动速度太快,并且提供者会抛出错误,因为我们太频繁地访问 API。我需要为请求设置一个时间间隔,但每次这样做都不起作用。有想法吗?
async function getReports(reportIDs) {
const urls = reportIDs.map(id => `https://api.data.com/api/v1/report/${id}/?include_datasets=true`);
const requests = urls.map(url => fetch(url, {
method: 'GET',
headers: { 'api-key': key }
}).then(res => res.json()));
const responses = await Promise.all(requests).catch(err => console.error(err));
return responses;
}
Run Code Online (Sandbox Code Playgroud)
我用一个承诺,这样我就可以await在另一个函数中使用该函数的结果来转换数据集。
有想法吗?
\xe2\x80\x9c简单是一种伟大的美德,但它需要努力工作才能实现,并需要接受教育才能欣赏它。更糟糕的是:复杂性卖得更好。\xe2\x80\x9d \xe2\x80\x94 Edsger W. Dijkstra
\n公认的“轻量级”解决方案近2万行代码,并且依赖于 CoffeeScript 和 Lua。如果您可以用所有这些来换取 50 行 JavaScript 代码会怎么样?
\n假设我们有一些job需要一些时间来计算一些结果 -
async function job(x) {\n // job consumes some time\n await sleep(rand(5000))\n // job computes a result\n return x * 10\n}\n\nPromise.all([1,2,3,4,5,6,7,8,9,10,11,12].map(job))\n .then(console.log, console.error)\nRun Code Online (Sandbox Code Playgroud)\n[10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]\nRun Code Online (Sandbox Code Playgroud)\n这会同时运行所有十二 (12) 个作业。如果这些是对远程的请求,则某些连接可能会被拒绝,因为过多的并发流量淹没了服务器。通过对Pool线程进行建模,我们控制并行作业的流程 -
// my pool with four threads\nconst pool = new Pool(4)\n\nasync function jobQueued(x) {\n // wait for pool thread\n const close = await pool.open()\n // run the job and close the thread upon completion\n return job(x).then(close)\n}\n\nPromise.all([1,2,3,4,5,6,7,8,9,10,11,12].map(jobQueued))\n .then(console.log, console.error)\nRun Code Online (Sandbox Code Playgroud)\n[10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]\nRun Code Online (Sandbox Code Playgroud)\n函数应该很小并且只做一件事。这使得编写单独的功能变得更加容易,并提高了可重用性,允许您将几个简单的功能组合成更复杂的功能。上面你已经看到rand了sleep-
const rand = x =>\n Math.random() * x\n\nconst sleep = ms =>\n new Promise(r => setTimeout(r, ms))\nRun Code Online (Sandbox Code Playgroud)\n如果我们想要throttle每项工作,我们可以专门sleep确保最短的运行时间 -
const throttle = (p, ms) =>\n Promise.all([ p, sleep(ms) ]).then(([ value, _ ]) => value)\n\nasync function jobQueued(x) {\n const close = await pool.open()\n // ensure job takes at least 3 seconds before freeing thread\n return throttle(job(x), 3000).then(close)\n}\n\nPromise.all([1,2,3,4,5,6,7,8,9,10,11,12].map(jobQueued))\n .then(console.log, console.error)\nRun Code Online (Sandbox Code Playgroud)\n[10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]\nRun Code Online (Sandbox Code Playgroud)\n我们可以添加一些console.log消息以确保一切正常运行。我们将sleep在作业的开头添加一个随机数,以表明任务可以按任意顺序排队,而不会影响结果的顺序 -
async function jobQueued(x) {\n await sleep(rand(5000))\n console.log("queueing", x)\n const close = await pool.open()\n console.log(" sending", x)\n const result = await throttle(job(x), 3000).then(close)\n console.log(" received", result)\n return result\n}\n\nPromise.all([1,2,3,4,5,6,7,8,9,10,11,12].map(jobQueued))\n .then(console.log, console.error)\nRun Code Online (Sandbox Code Playgroud)\n| 控制台日志 | 线程1 | 线程2 | 线程3 | 线程4 |
|---|---|---|---|---|
| 排队12 | \xe2\x8c\x9b | \xe2\x8c\x9b | \xe2\x8c\x9b | \xe2\x8c\x9b |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 12 | 打开 | \xe2\x8c\x9b | \xe2\x8c\x9b | \xe2\x8c\x9b |
| 排队9 | \xe2\x86\x93 | \xe2\x8c\x9b | \xe2\x8c\x9b | \xe2\x8c\x9b |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 9 | \xe2\x86\x93 | 打开 | \xe2\x8c\x9b | \xe2\x8c\x9b |
| 排队8 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x8c\x9b | \xe2\x8c\x9b |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 8 | \xe2\x86\x93 | \xe2\x86\x93 | 打开 | \xe2\x8c\x9b |
| 排队4 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x8c\x9b |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 4 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | 打开 |
| 排队 10 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| 排队6 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| 排队7 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| 排队2 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| 排队11 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 120 | 关闭 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 11 | 打开 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| 排队3 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| 排队5 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| 排队1 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 80 | \xe2\x86\x93 | \xe2\x86\x93 | 关闭 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 1 | \xe2\x86\x93 | \xe2\x86\x93 | 打开 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 90 | \xe2\x86\x93 | 关闭 | \xe2\x86\x93 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 5 | \xe2\x86\x93 | 打开 | \xe2\x86\x93 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 110 | 关闭 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 3 | 打开 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 40 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | 关闭 |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 2 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | 打开 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 10 | \xe2\x86\x93 | \xe2\x86\x93 | 关闭 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 7 | \xe2\x86\x93 | \xe2\x86\x93 | 打开 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 50 | \xe2\x86\x93 | 关闭 | \xe2\x86\x93 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 6 | \xe2\x86\x93 | 打开 | \xe2\x86\x93 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 20 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | 关闭 |
| \xc2\xa0\xc2\xa0\xc2\xa0发送 10 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 | 打开 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 30 | 关闭 | \xe2\x86\x93 | \xe2\x86\x93 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 70 | \xe2\x8c\x9b | \xe2\x86\x93 | 关闭 | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 60 | \xe2\x8c\x9b | 关闭 | \xe2\x8c\x9b | \xe2\x86\x93 |
| \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0收到 100 | \xe2\x8c\x9b | \xe2\x8c\x9b | \xe2\x8c\x9b | 关闭 |
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]\nRun Code Online (Sandbox Code Playgroud)\n上面,我们pool进行了初始化,size=4因此最多可以同时运行四个作业。在我们看sending四次之后,一项工作必须完成,并且我们received在下一项工作开始之前看到。queueing随时可能发生。您可能还注意到Pool使用高效的后进先出 (LIFO) 顺序处理排队作业,但结果的顺序保持不变。
继续我们的实现,就像我们的其他函数一样,我们可以thread用简单的方式编写 -
const effect = f => x =>\n (f(x), x)\n\nconst thread = close =>\n [new Promise(r => { close = effect(r) }), close]\n\nfunction main () {\n const [t, close] = thread()\n console.log("please wait...")\n setTimeout(close, 3000)\n return t.then(_ => "some result")\n}\n\nmain().then(console.log, console.error)Run Code Online (Sandbox Code Playgroud)\r\nplease wait...\n(3 seconds later)\nsome result\nRun Code Online (Sandbox Code Playgroud)\n现在我们可以用来thread编写更复杂的功能,例如Pool-
class Pool {\n constructor (size = 4) {\n Object.assign(this, { pool: new Set, stack: [], size })\n }\n open () {\n return this.pool.size < this.size\n ? this.deferNow()\n : this.deferStacked()\n }\n deferNow () {\n const [t, close] = thread()\n const p = t\n .then(_ => this.pool.delete(p))\n .then(_ => this.stack.length && this.stack.pop().close())\n this.pool.add(p)\n return close\n }\n deferStacked () {\n const [t, close] = thread()\n this.stack.push({ close })\n return t.then(_ => this.deferNow())\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n就这样你的程序就完成了。在下面的功能演示中,我浓缩了定义,以便我们可以立即看到它们。运行程序以在您自己的浏览器中验证结果 -
\nclass Pool {\n constructor (size = 4) { Object.assign(this, { pool: new Set, stack: [], size }) }\n open () { return this.pool.size < this.size ? this.deferNow() : this.deferStacked() }\n deferNow () { const [t, close] = thread(); const p = t.then(_ => this.pool.delete(p)).then(_ => this.stack.length && this.stack.pop().close()); this.pool.add(p); return close }\n deferStacked () { const [t, close] = thread(); this.stack.push({ close }); return t.then(_ => this.deferNow()) }\n}\nconst rand = x => Math.random() * x\nconst effect = f => x => (f(x), x)\nconst thread = close => [new Promise(r => { close = effect(r) }), close]\nconst sleep = ms => new Promise(r => setTimeout(r, ms))\nconst throttle = (p, ms) => Promise.all([ p, sleep(ms) ]).then(([ value, _ ]) => value)\n\nconst myJob = x => sleep(rand(5000)).then(_ => x * 10)\nconst pool = new Pool(4)\n\nasync function jobQueued(x) {\n await sleep(rand(5000))\n console.log("queueing", x)\n const close = await pool.open()\n console.log(" sending", x)\n const result = await throttle(myJob(x), 3000).then(close)\n console.log(" received", result)\n return result\n}\n\nPromise.all([1,2,3,4,5,6,7,8,9,10,11,12].map(jobQueued))\n .then(JSON.stringify)\n .then(console.log, console.error)Run Code Online (Sandbox Code Playgroud)\r\n.as-console-wrapper { min-height: 100%; }Run Code Online (Sandbox Code Playgroud)\r\n希望您学到了一些关于 JavaScript 的有趣知识!如果您喜欢这个,请尝试扩展Pool功能。也许添加一个简单的timeout函数来确保作业在一定的时间内完成。或者,也许添加一个retry函数,在作业产生错误或超时时重新运行该作业。要查看Pool是否适用于其他问题,请参阅此问答。如果您有任何疑问,我很乐意为您提供帮助:D