请教大家一个问题, js,异步执行

101 天前
 nbin2008
运行环境:nodejs
需求:提供一个方法 getD ,此方法内调用 ajax 接口获取数据,然后对外输出
1 、异步调用,比如会同时执行 30 次 getD
2 、当第一次调用 getD ,方法内没有数据,会从 ajax 拿数据,每次拿 10 条,等待中,当拿到数据后,用一条对外提供
3 、当第二次及后续调用 getD
a 如果有前面(第一次)正在 ajax 中,则等待,当 ajax 请求拿到数据后,第二次及后续的用拿到的数据
b 如果前面没有 ajax 中,则直接使用数据
4 、当 ajax 拿到的数据用完后,重复 2-3 ,但 ajax 每次请求间隔,须相隔 2s

不知道咋写,写出的也不满足,谢谢大家
2089 次点击
所在节点    JavaScript
14 条回复
InDom
101 天前
好像没啥难度?

做一个 对象,首先需要一 or 两个锁,然后需要一 or 两个队列。

发起 ajax 需要过一下锁,ajax 发出前锁定,ajax 成功后,锁设置过期时间 2s 。

getD 先检查队列是否能取到,成功就返回,失败就 ajax , 如果发出就等成功,失败就把回调推入等待队列。

ajax 成功后挨个讲结果回调给等待队列。
shiny
101 天前
这里要考虑一个问题,只有一个进程还是会有多个进程。如果是单进程的,直接用变量就能实现;多进程需要借助其他服务。

PS:你的描述够详细了,ChatGPT 写出来的就已经八九不离十了。
zbinlin
101 天前
用 async generator 来写试试
nbin2008
101 天前
@InDom 谢谢提供的思路,我想想
@shiny 谢谢,单进程
vampuke
101 天前
let pending;
const getId = async() => {
if (pending) return pending;
pending = new Promise((resolve) => {
setTimeout(() => {
resolve('id');
setTimeout(() => {
pending = null;
}, 2000);
}, 1000);
});
return pending;
}
Lhcfl
101 天前
不考虑多线程:
function makeGetD() {
let datas = [];
let promise = null;
let canNext = true;
const getD = async () => {
if (datas.length > 0) return datas.pop();
if (promise == null) {
if (!canNext) return;
canNext = false;
setTimeout(() => (canNext = true), 2000);
promise = new Promise(res => setTimeout(() => {
console.log("i'm ajax"); res([1,2,3,4,5,6,7,8,9,10]);
}, 300));
}
datas = await promise;
promise = null;
return await getD();
}
return getD;
}

getD = makeGetD();
Curtion
101 天前
nbin2008
101 天前
感谢大家帮忙,没来得及看,用 2 楼的方法解决了

const axios = require('axios');

let dataQueue = [];
let isFetching = false;

async function fetchData() {
if (isFetching) return;
isFetching = true;
try {
const response = await axios.get('https://example.com/api/data'); // 替换为实际的 API 地址
dataQueue = response.data.slice(0, 10); // 假设每次获取 10 条数据
} catch (error) {
console.error('Error fetching data:', error);
} finally {
isFetching = false;
}
}

async function getD() {
while (dataQueue.length === 0) {
if (!isFetching) {
await fetchData();
}
await new Promise(resolve => setTimeout(resolve, 100)); // 等待数据获取完成
}
return dataQueue.shift();
}

// 示例:同时执行 30 次 getD
(async () => {
const promises = Array.from({ length: 30 }, () => getD());
const results = await Promise.all(promises);
console.log(results);
})();
nbin2008
101 天前
@Lhcfl if (!canNext) return; 这里返回,就没有数据了
@Curtion 55 行调用,这里不是并发,整体代码很值得学习
谢谢楼上 v 友们的解答
Projection
100 天前
你说的这个用 Streams API 很容易实现:

```js
import { setTimeout } from "node:timers/promises";

let id = 0;

async function fetchData() {
console.log("fetchData()");
await setTimeout(Math.random() * 500);
return Array(10)
.fill(null)
.map(() => ({ id: ++id }));
}

const stream = new ReadableStream(
{
async pull(controller) {
const data = await fetchData();
for (const a of data) {
controller.enqueue(a);
}
},
},
new CountQueuingStrategy({ highWaterMark: 0 }), // fully lazy
).values();

async function getD() {
return (await stream.next()).value;
}

for (let i = 0; i < 30; i++) {
console.log("data", await getD());
}
```

输出结果:

fetchData()
data { id: 1 }
data { id: 2 }
data { id: 3 }
data { id: 4 }
data { id: 5 }
data { id: 6 }
data { id: 7 }
data { id: 8 }
data { id: 9 }
data { id: 10 }
fetchData()
data { id: 11 }
data { id: 12 }
data { id: 13 }
data { id: 14 }
data { id: 15 }
data { id: 16 }
data { id: 17 }
data { id: 18 }
data { id: 19 }
data { id: 20 }
fetchData()
data { id: 21 }
data { id: 22 }
data { id: 23 }
data { id: 24 }
data { id: 25 }
data { id: 26 }
data { id: 27 }
data { id: 28 }
data { id: 29 }
data { id: 30 }

这里为了定义你所需要 getD() 显得把代码搞复杂了一点,而实际上你可以根据情况使用 for await... of 。

你从 LLM 获得的代码有轮询的部分,所以强烈不推荐:

await new Promise(resolve => setTimeout(resolve, 100)); // 等待数据获取完成
jifengg
100 天前
综合看下来,我自己写的话应该也和 @Curtion 差不多。
你不需要一个 isFetching 来表示是否正在异步获取。如果不是你要求两次间隔 2s ,就根本不需要 setTimeout

当然,实际上你应该还要考虑 接口返回数据量是 0 的情况。
nbin2008
100 天前
@jifengg isFetching 作用,保证当时请求,始终只有一次
nbin2008
100 天前
@Projection 感谢,看的不是很懂,api 生疏
DOLLOR
100 天前
上面某人启发了我,Async Generator 就很容易实现,甚至不需要多余的全局变量,关键代码参见图中的 async function*

https://imgur.com/3n12UuC

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1059738

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX