Js 是单线程,js代码从上到下依次执行,比如我们写了两个函数,肯定是上面的函数先执行,下面的函数后执行。但是这种单线程有一个非常大的问题,那就是遇到耗时的任务,后面的任务只能等待它执行完,才能进行。比如ajax 请求,它从服务器上获取数据,这本来就耗时间, 如果网络再慢,就更耗时间,那么我们只能等待返回结果,结果出来之后再向下执行,等待的过程中,用户什么都不能做,如果是在渲染阶段,也会阻止渲染UI, 用户只能看到空白页面,体验太差。

  对于这种比较耗时间的任务怎么办,js 决定把它放到一边,先运行后面的任务,然后再回来处理这些耗时的任务。这就引入了异步的概念,因为我们书写代码的顺序和它执行顺序是不一致了。下面有三个函数,两个基本函数,和一个ajax 函数(作异步操作),我们的书写顺序是ajax -> add -> subtract, 但执行顺序却是 add -> sbutract -> ajax;

function add(num1,num2) {
    return num1 + num2;
}
function subtract(num1,num2) {
    return num2 - num1
}
function ajax() {
    var url = 'http://jsonplaceholder.typicode.com/posts/1';
    var xhr = new XMLHttpRequest();
    xhr.open("GET",url);
    xhr.onload= function () {
        console.log(xhr.responseText)
    }
    xhr.send()
}
ajax();
console.log(add(3,5))
console.log(subtract(3,5))

  这又引出了另 一个问题,异步的任务放到什么地方了?它以后回来执行,那什么时候执行?这里其实要注意一个问题,真正执行ajax操作的不是我们的js,而是浏览器,是我们的浏览器发出的http 请求。当js 执行到ajax 函数的时候,它其实是告诉浏览器去执行http 请求,然后就立即返回,执行它后面的代码,就是add 和substract  函数。那么浏览器执行完http请求,从服务器拿到了数据,怎么办?它这时执行我们的回调函数(onload 函数),就是把返回的数据传递给回调函数,然后把回调函数插入到事件(任务)队列中。js运行完add 和subtract 函数时,就没有事情可以做了,它就会去轮询事件(任务)队列,刚一轮询,它就发现,有一个onload 回调函数,那么它就会把它取出来执行这个函数。在程序运行过程中,js会一直执行轮询,直到程序结束。

  Js 代码在整个程序的运行过程中分为两个部分,一种是像add 函数这种,另一种是像ajax onload 回调函数这种,相对应js 运行也分成了两个部分,一个是主线程,一个是任务(异步回调函数)队列。

  js代码具体运行如下:

  1, js 代码一加载进来,它就会从上到下依次执行,它也就进入一个全局执行环境,当它遇到ajax 异步操作的时候,它会告诉浏览器去执行请求,然后立即返回,执行下面的代码,遇到add函数调用,它就会进入函数执行环境,执行完add函数时,它又遇到subtract 函数,它又会进入函数执行环境。当然这里比较简单,没有嵌套函数。如果函数中还嵌套一个函数,那么它就会进入子函数的执行环境,像下图一样,这就形成了一个执行栈,上面的执行后,再执行下面的,直到全局执行环境中的代码执行完毕,执行栈为空, 这就是就是js 的主线程。

  2,浏览器去执行http 请求, 在未来的一段时间内,它或者从服务器获得数据或者失败,这时它就会把我们注册的成功或失败的回调函数放入到任务(回调函数)队例中。

  3, 当执行栈中的所有代码执行完毕后,就是执行栈为空时,js就会去轮询我们的任务队列(左边图片),如果有任务,它就会从任务队列的起始位置 取出第一个任务(注册的回调函数)放到执行栈去执行(右边图片),等这个回调函数执行完毕后,执行栈再为空,js再去轮询我们的任务队列,如果还有回调函数,它再取出第一个放到执行栈执行。在整个程序的执行过程中,js 会一直轮询我们的任务队列,一有任务,就会执行,这就是事件循环。

  所有的程序都是栈中执行,只有执行栈空了,它有能力处理下一个任务。只要一个函数进入到执行栈,栈就不为空,栈不为空,就不能处理下一个函数,只能等到函数执行完毕。这就是所谓的 “run–to-complete ” 一次只能做一件事情。这也要求我们的回调函数中,不能执行太多任务,如果执行太多任务,栈就不为空,也就阻止了下一个任务的执行,造成阻塞。

以上所述,就是js 中的非阻塞。

JS 单线程和事件循环的更多相关文章

  1. JS执行机制--事件循环--笔记

    JS的解析是由浏览器中的JS解析引擎完成的.JS是单线程运行,也就是说,在同一个时间内只能做一件事,所有的任务都需要排队,前一个任务结束,后一个任务才能开始.但是又存在某些任务比较耗时,如IO读写等, ...

  2. js高级-浏览器事件循环机制Event Loop

    JavaScript 是队列的形式一个个执行的 同一时间只能执行一段代码,单线程的  (队列的数据结构) 浏览器是多线程的 JavaScript执行线程负责执行js代码 UI线程负责UI展示的 Jav ...

  3. 【nodejs原理&源码赏析(7)】【译】Node.js中的事件循环,定时器和process.nextTick

    [摘要] 官网博文翻译,nodejs中的定时器 示例代码托管在:http://www.github.com/dashnowords/blogs 原文地址:https://nodejs.org/en/d ...

  4. 【nodejs原理&源码赏析(7)】【译】Node.js中的事件循环,定时器和process.nextTick

    目录 Event Loop 是什么? Event Loop 基本解释 事件循环阶段概览 事件循环细节 timers pending callbacks poll阶段 check close callb ...

  5. js异步、事件循环(EventLoop)小结

    单线程 众所周知,JS是单线程的语言,之所以是单线程,用一句烂大街的话就是,如果两个线程同时操作一个DOM节点,那么该以哪个为准呢,虽然多线程也有办法解决,但是js毕竟是浏览器脚本语言,不需要那么复杂 ...

  6. 浅谈Javascript单线程和事件循环

    单线程 Javascript 是单线程的,意味着不会有其他线程来竞争.为什么是单线程呢? 假设 Javascript 是多线程的,有两个线程,分别对同一个元素进行操作: function change ...

  7. js 队列和事件循环

    1.示例代码 <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UT ...

  8. js event loop事件循环

    浏览器环境 以下两段代码是等价的.req对事件的回调设置,实际上就是当前主线程任务队列的任务. var req = new XMLHttpRequest(); req.open('GET', url) ...

  9. JS高阶---事件循环模式(事件轮询)

    大纲: 相关知识点: 主体: (1)模型原理 JS部分:初始化代码执行 WebAPIS:执行上下文对象(不是一个真的对象,而是一个抽象的虚拟对象,可以看做栈里的一个区域,包含很多对象) setTime ...

随机推荐

  1. ExFilePicker的使用 — 获取本地图片资源并用RecyclerView展示出来

    代码其实很简单,所以就不多进行文字说明,直接上完整的代码: 第一步:在app/build.gradle添加需要使用到的依赖库:(这里对引用的版本冲突问题作了处理,详情请看链接:https://www. ...

  2. X--名称空间详解

    转自:http://blog.csdn.net/lisenyang/article/details/18312039 X名称空间里面的成员(如X:Name,X:Class)都是写给XAML编译器看的. ...

  3. RPC通信原理(未完,先睡觉)

    一 背景 OpenStack 各组件之间是通过 REST 接口进行相互通信,比如Nova.Cinder.Neutron.Glance直间的通信都是通过keystone获取目标的endpoint,即ap ...

  4. day93之微信推送

    python之微信推送详解 用什么推送   -邮件   -微信推送   -短信推送微信推送   -公众号(不能主动给用户发消息)    -认证的公众号:需要营业执照,需要交钱,可以发多篇文章    - ...

  5. Django适合做大用户量的系统吗?

    分几点来答: 1. 首先,这其实是个技术选型题. 做技术选型的时候不能单纯的考虑性能,应该优先考虑业务类型,以及团队水平.另外的话,框架只是其中一环,还有配套呢. 如果是数据驱动型,尤其是要用到关系型 ...

  6. CRC-16 (Modbus)

    typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #defi ...

  7. Linux系统文件和目录管理

    Linux系统文件和目录管理 相关命令的解析 1.pwd:显示用户当前的工作目录 2.ls: -a:显示所有文件,包括隐藏文件 -l:显示文件的详细信息 3.设备文件统一存放在/dev 设备文件 块设 ...

  8. MySQL 数据库规范--调优篇(终结篇)

    前言 这篇是MySQL 数据库规范的最后一篇--调优篇,旨在提供我们发现系统性能变弱.MySQL系统参数调优,SQL脚本出现问题的精准定位与调优方法. 目录 1.MySQL 调优金字塔理论 2.MyS ...

  9. HashMap是如何工作的

    目录 1 HashMap在JAVA中的怎么工作的? 2 什么是哈希? 3 HashMap 中的 Node 类 4 键值对在 HashMap 中是如何存储的 5 哈希碰撞及其处理 6 HashMap 的 ...

  10. Django Rest framework基础使用之Request/Response

    1.Request restframework提供了一个Request对象(rest_framework.request.Request) Request对象继承了Django默认的HttpReque ...