问题图示如下
-
按钮A按了之后,ajax请求的数据显示在input type=text框里,B按钮也是。
-
问题就是如果先按A,此时ajax发出去了,但是数据还没返回来, 我们等不及了,马上按B按钮,结果此时A按钮请求的数据先回来,这就尴尬了,按的b按钮,结果先显示A按钮返回的数据,怎么解决?
这个问题的解释在我之前写的一篇文章,其中的第二题就是解决方https://juejin.cn/post/1,我们今天把这个问题展开
一、js的异步的运行机制是什么
以下是一张解释异步队列的图,以及文字说明(摘自阮一峰老师的博客)
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
这只是针对跟我一样中级水平或偏下的人普及一下JS异步的运行原理。之后,我们来看一个异步引发的问题代码
var res = [];
function response(data) {
res.push( data );
}
// ajax(..)是某个库中提供的某个Ajax函数
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
问题来了,我们假定期望的行为是res[0] 中放调用"http://some.url.1" 的结果,res[1] 中放调用"http://some.url.2" 的结果,改怎么办呢? 解决办法如下
var res = [];
function response(data) {
if (data.url == "http://some.url.1") {
res[0] = data;
}
else if (data.url == "http://some.url.2") {
res[1] = data;
}
}
// ajax(..)是某个库中提供的某个Ajax函数
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
上面这个场景用于多个并发函数共享DOM的问题,所以回调函数可以改进成这样的写法
var res = [];
function response(data) {
if (data.url == "http://some.url.1") {
//执行操作的函数,把参数data传入进去
callbackA(data)
}
else if (data.url == "http://some.url.2") {
//执行操作的函数,把参数data传入进去
callbackB(data)
}
}
// ajax(..)是某个库中提供的某个Ajax函数
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
好了,我们总结出一种处理并发共享DOM问题的解决方案,这个方案是我在一本叫《你不知道javascript 中卷》看到的,考官继续说,不用这种方法,因为要依赖后端发的数据要包含data.url,也就是url这个属性,怎样不依赖后端,前端独立解决这个问题呢? 在response上我们做一下改动,设置一个全局变量
var status; //值是undefined
我们在点击A按钮的时候, 让status的值变为A
status = "A";
我们在点击B按钮的时候,让status的值变为B
status = "B";
也就是resopnse改成这样:
function response(data) {
var status;
if(status = "A") {
// 点击A按钮status就变为“A”,所以不会执行按钮B的回调函数
执行 callbackA() A按钮的回调函数
}else if(status = "B"){
// 点击B按钮status就变为“B”,所以不会执行按钮A的回调函数
执行 callbackB() B按钮的回调函数
}
}
这样就解决了点A只显示A的数据,点B只显示B的数据的问题。
在这里我们继续延伸这个话题, 请看以下场景
var a,b;
function foo(x) {
a = x * 2;
baz();
}
function bar(y) {
b = y * 2;
baz()
}
function baz() {
console.log(a+b)
}
// ajax(..)是某个库中提供的某个Ajax函数
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
我们的目的是等a,b都异步请求回来才运行baz,解决办法如下
var a,b;
function foo(x) {
a = x * 2;
if(a && b) {
baz();
}
}
function bar(y) {
b = y * 2;
if(a && b) {
baz();
}
}
function baz() {
console.log(a+b)
}
// ajax(..)是某个库中提供的某个Ajax函数
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
接着,我们再换一个场景, 请看以下代码
var a;
function foo(x) {
a = x * 2
baz();
}
function bar(x) {
a = x / 2;
baz();
}
function baz() {
console.log(a)
}
// ajax(..)是某个库中提供的某个Ajax函数
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
a的值会改变两次,需求是只让a变一次,也就是第一次改变,第二次就不改变了
var a;
function foo(x) {
if(!a) {
a = x * 2;
baz();
}
}
function bar(y) {
if(!a) {
a = x / 2;
baz();
}
}
function baz() {
console.log(a)
}
// ajax(..)是某个库中提供的某个Ajax函数
ajax( "http://some.url.1", response );
ajax( "http://some.url.2", response );
好了,下面我要写一篇用promise解决异步问题的随笔(曾经看了一篇关于原生实现promise的文章,到时候也会简单介绍下一个简单的,但不是完全体的promise实现代码,只是为了大家更容易理解promise实现的内部大致原理),题目如下,需要20张图片,每次发出10个异步请求,请求10张图片,所以一共要请求两次,而且要求每次请求的10张图片是按顺序接收的,比如第一次发的请求是请求赵丽颖的图片,第二次发的请求是请求张三疯的图片,要求接收的顺序也是赵丽颖图片,张三疯图片 ...以此类推。