JavaScript 通过队列实现异步流控制
知乎上面看到一个面试题。
某个应用模块由文本框 input,以及按钮 A,按钮 B 组成。点击按钮 A,会向地址 urlA 发出一个 ajax 请求,并将返回的字符串填充到 input 中(覆盖 input 中原有的数据),点击按钮 B,会向地址 urlB 发出一个 ajax 请求,并将返回的字符串填充到 input 中(覆盖 input 中原有的数据)。
当用户依次点击按钮 A、B 的时候,预期的效果是 input 依次被 urlA、urlB 返回的数据填充,但是由于到 urlA 的请求返回比较慢,导致 urlB 返回的数据被 urlA 返回的数据覆盖了,与用户预期的顺序不一致。
请问如何设计代码,解决这个问题?
作者:欲三更
链接:https://zhuanlan.zhihu.com/p/25259283
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
网上搜了一下,找到的答案我认为不是特别好。如果这个拓展一下,变成依次点击按钮ABCDEFG,或者是BAAB来回点,就不好做了。
知乎下面有人说用Rxjs不到十行代码,我没试过。不过这 个毕竟是框架,库。能够原生解决,还是能学不少东西的。
我认为这个场景明显是适合用队列的。当时在思考中我先用队列粗暴的实现一个同步阻塞发送ajax的程序。后面我感觉应该再优化升级,异步非阻塞发送ajax,实现这个需求。我把的思路过程写下来。
我们用setTimeout模拟ajax,通过队列记录点击顺序,然后通过promise依次执行ajax。前一个ajax执行玩之后,再执行后一个ajax。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<input type="text"/>
<button id="domA">A</button>
<button id="domB">B</button>
</body>
<script>
const domA = document.querySelector('#domA')
const domB = document.querySelector('#domB')
const input = document.querySelector('input');
//模拟ajax
const ajaxA = () => new Promise((resolve, reject) => {
setTimeout(() => {
console.log('ajaxA end')
input.value = 'ajaxA end';
resolve()
}, 4000)
})
const ajaxB = () => new Promise((resolve, reject) => {
setTimeout(() => {
console.log('ajaxB end')
input.value = 'ajaxB end';
resolve()
}, 1000)
})
</script>
<script>
class Queue {
constructor() {
this.store = []
}
enqueue(ele) {
this.store.push(ele)
}
dequeue() {
return this.store.shift();
}
empty() {
if (this.store.length === 0) return true;
else return false;
}
}
const events = new Queue();
let flag = false;
const run = async () => {
flag = true;
while (!events.empty()) {
await events.dequeue()();
}
flag = false;
}
domA.addEventListener('click', () => {
events.enqueue(ajaxA);
if (!flag) { run() }
})
domB.addEventListener('click', () => {
events.enqueue(ajaxB);
if (!flag) { run() }
})
</script>
</html>
依次点击ABBA

Okay。解决了。但是让我想再升级一下问题。面试官说你这是同步阻塞发的ajax。我要不阻塞,点击了按钮就发ajax,不要浪费时间去等待ajaxA完成了再发ajaxB。显示的顺序还是按点击的顺序先A后B。
我们改一下程序,我们再点击了按钮之后立刻执行ajax(),这里个函数返回的是promise,把这个promise存入队列。队列执行过程中,登待promise的状态为resolve之后立刻就改变input的值。
由于ajaxB都已就绪,所以input的值改变非常迅速,肉眼已经跟不上了。我们在控制台打印出来。可以清晰看到ajax和input改变的顺序。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<input type="text"/>
<button id="domA">A</button>
<button id="domB">B</button>
</body>
<script>
const domA = document.querySelector('#domA')
const domB = document.querySelector('#domB')
const input = document.querySelector('input');
//模拟ajax
let value;
const ajaxA = () =>new Promise((resolve, reject) => {
setTimeout(() => {
console.log('ajaxA end')
resolve('ajaxA end')
}, 4000)
})
const ajaxB = () => new Promise((resolve, reject) =>{
setTimeout(() => {
console.log('ajaxB end')
resolve('ajaxB end')
}, 1000)
})
</script>
<script>
class Queue {
constructor() {
this.store = []
}
enqueue(ele) {
this.store.push(ele)
}
dequeue() {
return this.store.shift();
}
empty() {
if (this.store.length === 0) return true;
else return false;
}
}
const events = new Queue();
let flag = false;
const run = async () => {
flag = true;
while (!events.empty()) {
let event = events.dequeue();
input.value=await event;
console.log('input.value',input.value)
}
flag = false;
}
domA.addEventListener('click', () => {
events.enqueue(ajaxA());
if (!flag) { run() }
})
domB.addEventListener('click', () => {
events.enqueue(ajaxB());
if (!flag) { run() }
})
</script>
</html>
依次点击ABBA

JavaScript 通过队列实现异步流控制的更多相关文章
- JavaScript的同步与异步
1.手绘一张图说明. 2.为什么JavaScript是单线程(这里引用阮一峰老师的话) JavaScript的单线程,与它的用途有关. 作为浏览器脚本语言,JavaScript的主要用途是与用户互动, ...
- 数据结构与算法JavaScript (二) 队列
队列是只允许在一端进行插入操作,另一个进行删除操作的线性表,队列是一种先进先出(First-In-First-Out,FIFO)的数据结构 队列在程序程序设计中用的非常的频繁,因为javascript ...
- [js]javascript中4种异步
javascript中4种异步: 1.ajax 2.定时器 3.事件绑定 4,回调 定时器 //顺序执行 /* var s = 0; for (var i = 0; i < 10000; i++ ...
- c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)
c#封装DBHelper类 public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...
- Javascript的单线程和异步编程
运行时概念 下面的内容解释了一个理论上的模型.现代 JavaScript 引擎着重实现和优化了描述的几个语义. 可视化描述 栈 函数调用形成了一个栈帧. function foo(b) { var a ...
- 【读书笔记】iOS网络-同步请求,队列式异步请求,异步请求的区别
一,同步请求的最佳实践. 1,只在后台过程中使用同步请求,除非确定访问的是本地文件资源,否则请不要在主线程上使用. 2,只有在知道返回的数据不会超出应用的内存时才使用同步请求.记住,整个响应体都会位于 ...
- 掌握 Ajax,第 2 部分: 使用 JavaScript 和 Ajax 发出异步请求
转http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro2/ 掌握 Ajax,第 2 部分: 使用 JavaScript 和 Ajax 发出异步请求 ...
- 【读书笔记】iOS-网络-同步请求,队列式异步请求,异步请求的区别
一,同步请求的最佳实践. 1,只在后台过程中使用同步请求,除非确定访问的是本地文件资源,否则请不要在主线程上使用. 2,只有在知道返回的数据不会超出应用的内存时才使用同步请求.记住,整个响应体都会位于 ...
- JavaScript 文件延迟和异步加载
JavaScript 文件延迟和异步加载 -般情况下,在文档的 <head> 标签中包含 JavaScript 脚本,或者导入的 JavaScript 文件. 这意味着必须等到全部 Jav ...
随机推荐
- SDP(10):文本式大数据运算环境-MongoDB-Engine功能设计
为了让前面规划的互联网+数据平台能有效对电子商务数据进行管理及实现大数据统计功能,必须在平台上再增加一个MongDB-Engine:数据平台用户通过传入一种Context来指示MongoDB-Engi ...
- js运算符单竖杠“|”的用法和作用及js数据处理
js运算符单竖杠“|”的作用 很多朋友都对双竖杠“||”,了如指掌,因为这个经常用到.但是大家知道单竖杠吗?今天有个网友QQ问我,我的 javascript实用技巧,js小知识 , 这篇文章里面,js ...
- [BZOJ3680][JSOI2004]平衡点 / 吊打XXX
BZOJ Luogu (洛谷和BZOJ上的数据范围不同,可能需要稍微调一调参数) sol 这题的参数调得我心累 模拟退火的模型可以形象地理解为:不断降温的小球在一个凹凸不平的平面上反复横跳,根据万有引 ...
- [BZOJ4542] [Hnoi2016] 大数 (莫队)
Description 小 B 有一个很大的数 S,长度达到了 N 位:这个数可以看成是一个串,它可能有前导 0,例如00009312345.小B还有一个素数P.现在,小 B 提出了 M 个询问,每个 ...
- MySQL多数据源笔记2-Spring多数据源一主多从读写分离(手写)
一.为什么要进行读写分离呢? 因为数据库的"写操作"操作是比较耗时的(写上万条条数据到Mysql可能要1分钟分钟).但是数据库的"读操作"却比"写操作 ...
- Lintcode212 Space Replacement solution 题解
[题目描述] Write a method to replace all spaces in a string with%20. The string is given in a characters ...
- gnuplot画图中文标注相关问题
gnuplot是一个基于命令行的开源跨平台画图工具包,画图功能非常丰富.不过最近在考虑如何在gnuplot图中添加中文标注的过程中遇到了一些问题,记录如下. gnuplot支持多种的输出格式,比如pn ...
- celery学习之入门
Celery 简介 Celery 是一个简单.灵活且可靠的,处理大量消息的分布式系统,并且提供维护这样一个系统的必需工具.它是一个专注于实时处理的任务队列,同时也支持任务调度. broker:一个消息 ...
- Unity中List的随机排序(乱序)
为什么要给List排序做一个Unity限定条件呢 首先,是C#中的List泛型,若是Java,直接调用Collection.shuffle()就OK了 而Unity的C#版本较低,不能使用Random ...
- 第一周Python讲课内容--日记
1.python的发展史,由荷兰人Guido van Rossum于1989年发明,第一个公开发行版发行于1991年...... 2.第一个helloword程序的开始 3.变量的含义,赋值传参数的作 ...