知乎上面看到一个面试题
某个应用模块由文本框 input, 以及按钮 A, 按钮 B 组成点击按钮 A, 会向地址 urlA 发出一个 ajax 请求, 并将返回的字符串填充到 input 中 (覆盖 input 中原有的数据), 点击按钮 B, 会向地址 urlB 发出一个 ajax 请求, 并将返回的字符串填充到 input 中 (覆盖 input 中原有的数据)
当用户依次点击按钮 AB 的时候, 预期的效果是 input 依次被 urlAurlB 返回的数据填充, 但是由于到 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
来源: https://www.cnblogs.com/liaozhenting/p/8681527.html