JS异步的执行原理和回调详解

 更新时间:2021年3月8日 15:00  点击:2932

一、JS异步的执行原理

  我们知道JavaScript是单线程的,而浏览器是多线程的。单线程执行任务需要一个个排队进行,假如一个任务需要很长时间执行(像ajax需要较长时间),会直接导致无响应,后面的任务一直在等待执行。这时候就需要用到异步。

  想了解异步,首先我们要知道浏览器有最基本的三个常驻线程: JS引擎线程,事件触发线程,GUI渲染线程。

  其中JS引擎线程和事件触发线程共同构成了一种事件循环机制,而GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新保存在一个队列中,当JS引擎空闲时,立即被执行。

  我们从它的事件循环机制解析:

  JS引擎线程中分为同步和异步任务:

    1.同步任务全部通过主线程执行,形成执行栈。

    2.当有异步任务时交给异步进程(WebAPIs):包含事件触发线程或者定时器线程等处理,形成任务队列。

    3.当执行栈中的任务全部处理完成,主线程为空闲的时候,会从任务队列中提取任务到执行栈中执行。

  通俗来说,JavaScript除了主线程之外还存在一个任务队列,任务队列存放需要异步执行的内容,执行完主线程后,就会不断循环扫描执行任务队列的任务,直至队列清空。

画解:

  如图小明因为学习耗时长会,如果没做完就会一直无法玩DNF游戏了,就把学习放到了异步任务队列中,等玩完游戏(主线程)再学习(任务队列)。期间母亲添加学习事件(DOM事件),小明每完成一个学习任务就看看还有啥任务(循环扫描),直至最后做完.

  下面再看一个例子(浏览器刷新不断点击按钮):

  let myData = null
  //ajax请求
  function ajax() {
  //腾讯新冠实时数据接口,仅做学习
  axios.get('https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=chinaDayList,chinaDayAddList,nowConfirmStatis,provinceCompare')
   .then(data => {
   console.log("ajax返回成功");
   myData = data.data
   console.log(myData);

   })
   .catch(error => {
   console.log("ajax返回失败");
   })
  }
  console.log(myData);
  ajax()
  setTimeout(() => {
  console.log('定时器');
  }, 2000);
  console.log(myData);
  const btn = document.querySelector('button')
  btn.onclick = () => {
  console.log("点击了");
  }

null
null
ajax返回成功
Object
点击了
定时器
点击了

  可以看到,console在主线程中是同步执行的,先执行,而在主线程外的任务队列,存放着异步执行的内容,这里是setTimeout,ajax和DOM事件,按照任务队列顺序执行(循环扫描队列)。

  为什么要循环扫描呢?

  通过点击事件可以看出,当用户进行交互时(点击事件,滚动事件,窗口大小变化事件等),会向事件循环中的任务队列添加新事件,然后等待执行,所以需要循环扫描。

二、JS异步中的回调

  既然异步都是放在最后的任务队列执行,那么我们很多逻辑就难以实现,这时候我们需要处理这种异步逻辑,最常用的方式是回调——回头调用。

回调函数:简单来说就是,函数A中传入函数B作为参数时,函数B即为A函数执行的回调函数。回调有嵌套回调和链式回调两种。

  下面是回调的一个简单用法:

   let myData = null
   console.log(myData);
   setTimeout(() => {
    console.log('定时器');
   }, 2000);
   const btn = document.querySelector('button')
   btn.onclick = () => {
    console.log("点击了");
   }
   let name = "张三"
   function hr(callback) {
    setTimeout(() => {
     console.log(`我是${name}`);
     callback();
    }, 2001);
   }
   console.log(myData);
   function gj() {
    console.log(`${name}你好,我是李四,认识一下吧`);
   }
   hr(gj)

null
null
点击了
定时器
我是张三
张三你好,我是李四,认识一下吧
点击了

  很明显的看到,当我们函数需要用到数据的时候就用到了回调,这里用到的是异步回调。

  回调虽然是解决异步常用的方法,可是伴随着JS日益复杂的需求。同步异步需要越来越多的回调实现逻辑。同异步的混杂和过多的回调嵌套和缩进使得代码变得难以解读和维护,形成“回调地狱”。

  我们看一个例子:

const verifyUser = function(username, password, callback){
  dataBase.verifyUser(username, password, (error, userInfo) => {
    if (error) {
      callback(error)
    }else{
      dataBase.getRoles(username, (error, roles) => {
        if (error){
          callback(error)
        }else {
          dataBase.logAccess(username, (error) => {
            if (error){
              callback(error);
            }else{
              callback(null, userInfo, roles);
            }
          })
        }
      })
    }
  })
};

大多数人光是看到上面的代码就感受到了脑子冻结的滋味,如果一个项目里拥有上百个这样的代码块,过一段时间,我相信连编写他的人都会头疼。来到自己的项目就像是来到了地狱。

  最主要的是,与此同时回调还存在信任问题,他把执行控制权交给了某个第三方(比如ajax)。为了解决信任问题,我们必须在程序写各种逻辑来解决回调带来的信任问题。

  ·调用过早

  ·调用过完

  ·调用次数过多过少,没有把需要的参数成功传给回调函数,

  ·可能出现的错误被吞。

  可以发现写特定逻辑来解决特定的信任问题,已经使得难度大于本身应用价值了,还会造成代码冗杂,可读性差等问题。

  综上:回调解决异步存在缺陷:

     1)不符合人对任务处理的逻辑思维

     2)回调带来的信任问题。

  面对回调日益明显的弊端,ES6更新了Promise用来解决异步问题。下一篇写ES6——Promise。

总结

到此这篇关于JS异步的执行原理和回调的文章就介绍到这了,更多相关JS异步执行原理回调内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • JavaScript判断浏览器及其版本信息

    本篇文章主要分享了通过window.navigator来判断浏览器及其版本信息的实例代码。具有一定的参考价值,下面跟着小编一起来看下吧...2017-01-23
  • js实现浏览器打印功能的示例代码

    这篇文章主要介绍了js如何实现浏览器打印功能,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-15
  • Nest.js参数校验和自定义返回数据格式详解

    这篇文章主要给大家介绍了关于Nest.js参数校验和自定义返回数据格式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-28
  • 利用JS实现点击按钮后图片自动切换的简单方法

    下面小编就为大家带来一篇利用JS实现点击按钮后图片自动切换的简单方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-10-25
  • 详解前端安全之JavaScript防http劫持与XSS

    作为前端,一直以来都知道HTTP劫持与XSS跨站脚本、CSRF跨站请求伪造。防御这些劫持最好的方法是从后端入手,前端能做的太少。而且由于源码的暴露,攻击者很容易绕过防御手段。但这不代表我们去了解这块的相关知识是没意义的,本文的许多方法,用在其他方面也是大有作用。...2021-05-24
  • js实现调用网络摄像头及常见错误处理

    这篇文章主要介绍了js实现调用网络摄像头及常见错误处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-07
  • JS实现随机生成验证码

    这篇文章主要为大家详细介绍了JS实现随机生成验证码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-06
  • js实现上传图片及时预览

    这篇文章主要为大家详细介绍了js实现上传图片及时预览的相关资料,具有一定的参考价值,感兴趣的朋友可以参考一下...2016-05-09
  • js组件SlotMachine实现图片切换效果制作抽奖系统

    这篇文章主要介绍了js组件SlotMachine实现图片切换效果制作抽奖系统的相关资料,需要的朋友可以参考下...2016-04-19
  • vue.js 表格分页ajax 异步加载数据

    Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统.这篇文章主要介绍了vue.js 表格分页ajax 异步加载数据的相关资料,需要的朋友可以参考下...2016-10-20
  • js实现列表按字母排序

    这篇文章主要为大家详细介绍了js实现列表按字母排序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-08-11
  • 基于JavaScript实现文字超出部分隐藏

    这篇文章主要介绍了基于JavaScript实现文字超出部分隐藏 的相关资料,需要的朋友可以参考下...2016-03-01
  • js+css实现回到顶部按钮(back to top)

    这篇文章主要为大家详细介绍了js+css实现回到顶部按钮back to top回到顶部按钮,感兴趣的小伙伴们可以参考一下...2016-03-03
  • NodeJS实现阿里大鱼短信通知发送

    本文给大家介绍的是nodejs实现使用阿里大鱼短信API发送消息的方法和代码,有需要的小伙伴可以参考下。...2016-01-20
  • JS实现响应鼠标点击动画渐变弹出层效果代码

    这篇文章主要介绍了JS实现响应鼠标点击动画渐变弹出层效果代码,具有非常自然流畅的动画过度效果,涉及JavaScript针对鼠标事件的响应及页面元素样式的动态操作相关技巧,需要的朋友可以参考下...2016-03-28
  • 浅析AngularJS Filter用法

    系统的学习了一下angularjs,发现angularjs的有些思想根php的模块smarty很像,例如数据绑定,filter。如果对smarty比较熟悉的话,学习angularjs会比较容易一点,这篇文章给大家介绍angularjs filter用法详解,感兴趣的朋友一起学习吧...2015-12-29
  • node.JS md5加密中文与php结果不一致怎么办

    这次文章要给大家介绍的是node.JS md5加密中文与php结果不一致怎么办,不知道具体解决办法的下面跟小编一起来看看。 因项目需要,需要Node.js与PHP做接口调用,发现nod...2017-07-06
  • JavaScript仿支付宝密码输入框

    那么今天我就用JavaScript代码来实现这个效果吧,那么首先介绍一下整个的思路,首先我们先将确定输入密码的位数,我的需求是5位,那么就用一个div标签包住5个input标签...2016-01-02
  • 一个关于JS正则匹配的踩坑记录

    这篇文章主要给大家介绍了一个关于JS正则匹配的踩坑记录,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-13
  • 基于JavaScript实现表单密码的隐藏和显示出来

    为了网站的安全性,很多朋友都把密码设的比较复杂,但是如何密码不能明显示,不知道输的是对是错,为了安全起见可以把密码显示的,那么基于js代码如何实现的呢?下面通过本文给大家介绍JavaScript实现表单密码的隐藏和显示,需要的朋友参考下...2016-03-03