多个 AJAX 请求,分别为 N1,N2,N3,N4,N5
N1 请求完成之后输出 console.log(1) N2 请求完成之后输出 console.log(2) 依此类推
如果 N1,N2,N3,N4,N5 依次执行,但是返回时间不确定,如何使最终输出的只有 5
这里的请求数量可能会很多,五个只是举例。 前者的请求无法取消
1
mxT52CRuqR6o5 2022-08-18 16:35:53 +08:00
简单一点的,保存上一个的 abortController ,下一次请求发出去前把上一个请求 abort 掉
复杂一点的,rxjs |
2
westoy 2022-08-18 16:36:18 +08:00 1
一般数据都会记录一个 updated_at 的
返回的时候和之前那次比较, 取新的 |
3
Kenmin 2022-08-18 16:36:37 +08:00
善用 async await
|
4
doommm 2022-08-18 16:38:47 +08:00 1
我选 rxjs
|
5
ifbluethen 2022-08-18 16:39:35 +08:00
for(var c = 0; c < 5; c++) {
setTimeout(function() { console.log(c); }, 1000); } |
6
kop1989smurf 2022-08-18 16:39:41 +08:00
不懂这个“依次执行”的意思到底是什么。
1 、如果顺序固定,那么前面请求的意义是什么? 2 、如果顺序不固定,那么到底哪个请求才是“N5”?是花费时间最长的那个么? |
7
zhanghx1991 2022-08-18 16:42:37 +08:00
我也选 rxjs
|
8
shyling 2022-08-18 16:43:55 +08:00
没看懂需求。。。
Promise.all 满足不了你? 知道要 5 不能只在 5log ? |
9
ITsWHY 2022-08-18 16:44:15 +08:00
每个请求设置一个 id 再用一个全局的变量记录最后一个发的 id, if(全局 id != 当前请求 id) 就直接 return
|
10
yxcoder OP @kop1989smurf 顺序执行,没有意义,用户的操作有啥意义可言呢,重要的是解决办法
|
11
ytll21 2022-08-18 16:44:54 +08:00
每个请求给一个编号,返回的值压入一个 array 中,按照编号排序,取最大编号的值。
或者再简单点,只保留一个值,只有当编号大于保留的值的编号时,才做更换。 |
12
Terry05 2022-08-18 16:45:10 +08:00 1
感觉你这种类似在短时间里一直轮询的场景,这种情况下如果下一个时间周期,上一个周期的请求还没完成,可以强制 cancel 各种 http request 库都有类似功能
|
13
woodensail 2022-08-18 16:46:08 +08:00 1
并发控制有几种不同的处理,可以根据不同需求选用。
1:发起新请求时如果有进行中的请求,则直接将老请求的 promise 丢回去。这种方式适用于不需要考虑时效性的请求接口。 2:乐观锁,每次进入长逻辑时将乐观锁+1 然后记录当前的值,逻辑执行中每个异步任务完成后都检查一次乐观锁是否变动,如果没变则可以继续执行,如果变了,则终止当前人物。这个方式适用于包含多个异步人物的长逻辑链条,且允许新操作覆盖旧操作的场景 3:简单的互斥锁,有进行中的请求则将新的操作废弃或者排队。一般提交类操作这么搞,前一个请求完成前,不允许发起第二个请求 这几个是我常用的手段,还有其他手段可以参考其他人的回复,比如上面说到的 abort 老的请求。 |
14
jamosLi 2022-08-18 16:49:22 +08:00
建议说场景 不说场景那就干掉异步
|
15
yxcoder OP @mxT52CRuqR6o5 请求没法拦截掉,该走的逻辑还是会走,现在的问题是如何让它知道它后面又有个请求发出去了
@westoy 好像是可以,用时间戳做标志位,似乎可以解决,我试一下 @shyling 这里的 5 只是一个例子,可能有很多个,而且这些请求并不是预先就知道的,可能是前一个请求发到一半,来了另一个请求 @ITsWHY 没法为每一个请求记录一个 ID ,因为请求的数量其实是不固定的 |
16
kop1989smurf 2022-08-18 16:54:17 +08:00
|
17
yxcoder OP @woodensail 第二种方法应该是 ok 的,和前面一个说时间戳的其实是一个道理
|
18
yxcoder OP @kop1989smurf 你可以看下 2 楼和 13 楼的方案,你说的 1 对应的是互斥锁,2 方法在提问中就已经说明了前者的请求无法取消。可以使用乐观锁或者时间戳解决
|
19
keepeye 2022-08-18 17:01:16 +08:00
一个计数器就可以搞定的
let c = 0 function req() { let n = c++ // .... 请求过程 if (n == c) { console.log("....") } } |
21
edward1987 2022-08-18 17:08:54 +08:00
// 用闭包变量 res 来承载同一种请求的结果,一旦有新的请求,所有的 req()返回的都指向最新的结果
let res; function req(){ res = doRequest() return res } async function api(){ const lastRes = await req() } |
22
del1214 2022-08-18 17:13:23 +08:00
redux saga takelatest
|
23
edward1987 2022-08-18 17:16:21 +08:00
@edward1987 有点问题 修正下
// 用闭包变量 res 来承载同一种请求的结果,一旦有新的请求,所有的 req()返回的都指向最新的结果 let res; async function req(){ res = doRequest(); await res; return res; } async function api(){ const lastRes = await req() } |
24
dudubaba 2022-08-18 17:17:26 +08:00
封装一个 reduce 就搞定了
|
25
dcsuibian 2022-08-18 17:21:51 +08:00 via Android
说一下使用场景
怕的就是 xy 问题 |
26
qzhai 2022-08-18 17:28:58 +08:00
@shyling
@kop1989smurf 楼主的意思应该是,一个 ajax 的分页 有 5 页,用户依次点击 1 - 5 页的按钮,最终留在某一页,这个时候 ajax 发了 5 此,正常不处理的话,最后一个返回的会 承接当前列表。 |
27
qiayue 2022-08-18 17:46:03 +08:00
用一个数组记录每一次的返回结果,再根据前端的状态决定显示哪个结果。
拿分页这个例子来说的话,如果 5 页数据都返回了,那么之后用户点击任何页面,都不需要再发送请求了,直接从已返回结果里拿数据并显示就好了。 |
28
wunonglin 2022-08-18 17:49:50 +08:00
rxjs 两三行就可以了。搞那么多乱七八糟的
|
29
rrfeng 2022-08-18 17:53:29 +08:00
建议描述下原始需求,console.log 什么的代表不了什么
|
31
wunonglin 2022-08-18 18:23:35 +08:00 1
|
32
Vegetable 2022-08-18 18:23:38 +08:00
看了你的描述场景,我觉得吧
你直接把展示的价格和展示商品的 ID 做一个映射,显示哪个商品就展示哪个价格,费这么大劲操作请求属于有点把问题搞复杂了 |
33
plusor 2022-08-18 18:32:17 +08:00
throttle?
|
34
chnwillliu 2022-08-18 18:38:00 +08:00 via Android
对,rxjs 下 switchMap 很简单。
|
35
dtdths1 2022-08-18 19:19:30 +08:00
最简单的办法就是成功回调时跟最后提交的 id 对比一下,一样再渲染
|
36
chnwillliu 2022-08-18 19:24:18 +08:00 via Android 1
就是典型的 switchMap 场景,自动 unsubscribe 上一次产生的流,自动切到最新的流上去。
id$ = new Subject(); price$ = id$.pipe( switchMap(id => getPriceById(id)) ) // merge + map 很干净,省一个 subject isLoading$ = merge( id$.pipe(mapTo(true)), price$.pipe(mapTo(false)) ); getPriceById 需要返回一个 observable, unsubscribe 时 abort 请求即可。 切商品直接 id$.next(newId), price$ 和 isLoading$ 会自动更新。上一次没完成的请求 switchMap 会自动 unsubscribe ,简直毫无负担。 |
37
yxcoder OP |
38
markgor 2022-08-19 10:36:40 +08:00
我不是专业前端,
但是这个需求不是应该是节流和防抖的事吗.... 另外 ajax 请求是可以 abort 的, 我没理解错的话你意思是 前端快速切换商品,但是由于 ajax 是异步请求,导致最终渲染出来的结果并非最后客户选择的产品结果。 我觉得这种场景上节流,请求异步改同步就能很好解决了, 如果为了体验,可以上骨架,请求前开始骨架渲染,结果返回后取消骨架渲染替换真实结果。 |
39
yxcoder OP @markgor
1.节流需要获取句柄,暂无法获取 2.受限于框架,无法使用 abort ,问题中已经说过了 3.JS 是单线程,异步改同步会阻塞 JS 进程 4.骨架和文案 “价格查询中...” 有什么本质区别吗? |
40
luvxy 2022-08-19 10:54:01 +08:00
promise.all 就行了 等待所有请求完毕 才会给你返回所有结果
|
41
kiritoxf 2022-08-19 11:17:01 +08:00
看场景感觉像是竞态问题
|
42
jihu777 2022-08-19 13:02:32 +08:00 via Android
rxjs ,switchMap
|
43
thulof 2022-08-19 14:37:03 +08:00
应该是竞态问题。记录最新请求的 id ,如果返回结果携带的 id 不匹配,则忽略
|