JS是单线程
单线程:排队,所有的任务工作都需要进行先排队,前一个任务完成了之后才会执行下一个任务,如果前一个任务所需的事件很长,后一个任务就不得不一直等待。
单线程导致的问题是,如果一个JS执行的时间过长,这样就会导致页面的渲染不连贯。
JS语言的最大特点之一就是单线程,单线程的核心概念是指:同一个时间只能做一件事。原因是因为JS脚本语言的初衷所导致(JS是为了实现处理页面中的用户交互),以及操作DOM。
在DOM操作中已经充分展示了单线程特征:创建一个元素,创建成功之后才可以将它添加到某个节点中。
同步任务和异步任务
单线程导致的问题就是延时任务之后的任务要等待,如果延时任务耗时比较长,后面的任务就会一直等待
解决这个问题的方案:利用计算机CPU的多核特征进行解决,HTML5提出Web Worker标准,允许JS创建出多个线程,但是创建出来的所有子线程全部受制于主线程,这样实现了同步和异步的任务
同步
前一个任务结束后在执行后一个任务,程序执行的顺序和任务的排列顺序是一致的。同步的做法例如:吃饭吃完饭才能睡觉
异步
在做一件事的时候,因为这个任务花费时间比较长,在做这件事的时候,还可以去做另一件事,比如一边吃饭一边听音乐
本质区别:这条流水线上的各个流程的执行顺序不同
JS中的所有任务分两种:同步任务和异步任务
同步任务指的是:主线程上的任务排队执行
异步任务指的是:不进入主线程,而是进入一个”任务队列“的任务,当主线程中的任务执行完,才会从任务队列中取出异步任务放入主线程执行,一般被判断为耗时的任务都会加入任务队列,也就是异步,如setTimeout等
同步任务
将所有的任务在主线程中拍好队然后形成一个执行队列,一个一个执行处理
异步任务
JS中的异步任务是通过回调函数实现的
回调函数callback:当一个任务执行完成之后回过头来调用的这个函数
一般而言,异步任务有三种:
普通事件,click、resize事件等
资源加载,如load,error等
定时器,setTimeout,setInterval等
异步任务相关的回调函数添加到任务列表中(消息队列)
JS执行机制(事件循环)
先执行主线程(执行栈)中的同步任务
异步任务(回调函数)放入到任务队列中
一旦执行栈中的所有同步任务执行完毕,系统就会自动按照次序读取任务队列中的异步任务,通过这种方式被读取的异步任务结束等待过程,进入到执行栈中,开始执行
具体来说,异步运行机制如下:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
(3)一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
只要主线程空了,就会去读取”任务队列”,这就是JavaScript的运行机制。这个过程会不断重复。
“任务队列”是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在”任务队列”中添加一个事件,表示相关的异步任务可以进入”执行栈”了。主线程读取”任务队列”,就是读取里面有哪些事件。
“任务队列”中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等),比如$(selectot).click(function),这些都是相对耗时的操作。只要指定过这些事件的回调函数,这些事件发生时就会进入”任务队列”,等待主线程读取。
所谓”回调函数”(callback),就是那些会被主线程挂起来的代码,前面说的点击事件$(selectot).click(function)中的function就是一个回调函数。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。例如ajax的success,complete,error也都指定了各自的回调函数,这些函数就会加入“任务队列”中,等待执行。
火狐浏览器的api文档有这样一句话:
尽管setTimeout的time延迟时间为0,其中的function也会被放入一个队列中,等待下一个机会执行,当前的代码(指不需要加入队列中的程序)必须在该队列的程序完成之前完成,因此结果可能不与预期结果相同。
这里说到了一个“队列”(即任务队列),放的就是setTimeout中的function,这些function依次加入该队列,即该队列中所有function中的程序将会在该队列以外的所有代码执行完毕之后再以此执行,这是为什么呢?因为在执行程序的时候,浏览器会默认setTimeout以及ajax请求这一类的方法都是耗时程序(尽管可能不耗时),将其加入一个队列中,该队列是一个存储耗时程序的队列,在所有不耗时程序执行过后,再来依次执行该队列中的程序。