EmberYu / vic-blog

9 stars 0 forks source link

让异步队列按照调用顺序返回 #17

Open EmberYu opened 5 years ago

EmberYu commented 5 years ago

昨天晚上小伙伴发了我一个文章,标题很吓人,叫如果你想靠前端技术还房贷,你不能连这个都不会,刚好戳中了我这个潜在富一代买不起房的痛点。吓得我赶紧点进来看了一下,吭哧吭哧大概花了三个小时才搞出来,做完又开始发愁了,房贷可能交得起了,这首付是国家发还是自己领呢。
好了不扯了回到正题

问题

知乎地址 https://zhuanlan.zhihu.com/p/25259283 某个应用模块由文本框 input,以及按钮 A,按钮 B 组成。点击按钮 A,会向地址 urlA 发出一个 ajax 请求,并将返回的字符串填充到 input 中(覆盖 input 中原有的数据),点击按钮 B,会向地址 urlB 发出一个 ajax 请求,并将返回的字符串填充到 input 中(覆盖 input 中原有的数据)。
当用户依次点击按钮 A、B 的时候,预期的效果是 input 依次被 urlA、urlB 返回的数据填充,但是由于到 urlA 的请求返回比较慢,导致 urlB 返回的数据被 urlA 返回的数据覆盖了,与用户预期的顺序不一致。
请问如何设计代码,解决这个问题?

建议可以自己先尝试一下看能不能写出来
想看答案的可以直接戳这里

问题分析

首先这道题肯定是一个将异步队列按同步的顺序返回的问题。先返回的如果不是队列的第一个任务,那么结果将会被保留下来,一直等到队列第一个任务返回时出栈,轮到自己到第一个队列时才执行。

首先我们先写html页面

image
html很简单,第一个按钮点击后等3s返回结果。第二个按钮点击1s后返回结果。为了让效果更明显,我们将返回的结果依次输出在container这个class

然后我们写js代码

我们首先模拟两个请求
image 正常情况下我们的请求一般都是这样的,返回一个promise。接下来看一下我们对两个按钮进行方法绑定
image 这样就完成了按钮绑定,将返回的结果输出到页面中这个方法,但是由于异步返回时间的不同,他们的顺序并不是按照点击的顺序来的,这里就到了问题的难点了。如何让异步的请求按照同步的结果返回?问题大概可以归纳为几个步骤

  1. 首先要有一个执行队列,队列中的内容即为保存的请求
  2. 当保存的请求执行完毕时,给它设置一个执行完毕的状态。同时触发一个执行完毕的方法
  3. 在执行完毕的方法中,检查队列第一个索引中的内容是否执行完毕,如果执行完毕则执行后续代码并移出队列。判断第二个是否也执行完毕,以此类推。
    代码如下
    image 这里可以看到,我们用了一个createSyncTask方法,用高阶函数的方法,将异步的队列传入,并返回一个组装后的高阶函数。当调用这个高阶函数时,就向taskQueuepush进一个任务。同时让它的Promise一直处于pending状态,当执行完毕后,才改为resolve。同时触发队列中已经有任务完成的方法triggerTaskFinish
    triggerTaskFinish方法就很简单了。判断当前队列第一个是否已经resolve,如果resolve。那么就将它的Promise方法状态设置为resolve,同时将返回值传入。
    那么接下来,我们只需要将原来的异步请求包装一下即可实现需求
    image
    好了,那么我们就可以实现任务是根据我们点击顺序来实现的效果了。而且这种写法,如果不需要同步了或者增加新的按钮,后面改起来也比较方便。

完整js代码如下
image

如果有什么不足的地方欢迎评论指正。毕竟我也只是个小菜鸡