由一道JS异步面试题,思考对异步问题的处理(1,暂时不用promise)

问题图示如下

再进一步说明问题

  • 按钮A按了之后,ajax请求的数据显示在input type=text框里,B按钮也是。

  • 问题就是如果先按A,此时ajax发出去了,但是数据还没返回来, 我们等不及了,马上按B按钮,结果此时A按钮请求的数据先回来,这就尴尬了,按的b按钮,结果先显示A按钮返回的数据,怎么解决?

这个问题的解释在我之前写的一篇文章,其中的第二题就是解决方https://juejin.im/post/5a1810b56fb9a0452405854c,我们今天把这个问题展开

一、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张图片是按顺序接收的,比如第一次发的请求是请求赵丽颖的图片,第二次发的请求是请求张三疯的图片,要求接收的顺序也是赵丽颖图片,张三疯图片 ...以此类推。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值