`

js异步和同步

 
阅读更多

一、为什么JavaScript是单线程?

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

二、任务队列

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。

JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。

于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断重复上面的第三步。

下图就是主线程和任务队列的示意图。

任务队列

只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。

三、事件和回调函数

"任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。

"任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。

所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。

"任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于存在后文提到的"定时器"功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。

四、Event Loop

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

为了更好地理解Event Loop,请看下图(转引自Philip Roberts的演讲《Help, I'm stuck in an event-loop》)。

Event Loop

上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。

执行栈中的代码(同步任务),总是在读取"任务队列"(异步任务)之前执行。请看下面这个例子。


    var req = new XMLHttpRequest();
    req.open('GET', url);    
    req.onload = function (){};    
    req.onerror = function (){};    
    req.send();

上面代码中的req.send方法是Ajax操作向服务器发送数据,它是一个异步任务,意味着只有当前脚本的所有代码执行完,系统才会去读取"任务队列"。所以,它与下面的写法等价。


    var req = new XMLHttpRequest();
    req.open('GET', url);
    req.send();
    req.onload = function (){};    
    req.onerror = function (){};   

也就是说,指定回调函数的部分(onload和onerror),在send()方法的前面或后面无关紧要,因为它们属于执行栈的一部分,系统总是执行完它们,才会去读取"任务队列"。

五、定时器

除了放置异步任务的事件,"任务队列"还可以放置定时事件,即指定某些代码在多少时间之后执行。这叫做"定时器"(timer)功能,也就是定时执行的代码。

定时器功能主要由setTimeout()和setInterval()这两个函数来完成,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。以下主要讨论setTimeout()。

setTimeout()接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数。


console.log(1);
setTimeout(function(){console.log(2);},1000);
console.log(3);

上面代码的执行结果是1,3,2,因为setTimeout()将第二行推迟到1000毫秒之后执行。

如果将setTimeout()的第二个参数设为0,就表示当前代码执行完(执行栈清空)以后,立即执行(0毫秒间隔)指定的回调函数。


setTimeout(function(){console.log(1);}, 0);
console.log(2);

上面代码的执行结果总是2,1,因为只有在执行完第二行以后,系统才会去执行"任务队列"中的回调函数。

总之,setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。

HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()。

需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。

分享到:
评论

相关推荐

    Angular异步变同步处理方法

    首先,异步编程是JavaScript的核心特征之一,它允许JavaScript引擎在等待一个长时间操作(如网络请求)完成时,继续执行其他任务。异步操作典型地使用回调函数(callback)来处理操作完成时的行为,但过多的嵌套(回...

    Nodejs让异步变成同步的方法

    co是一个流行的用于JavaScript的Promise和生成器(Generators)的控制流库,它可以使异步代码看起来像同步代码。co库之所以能够做到这一点,是因为它利用了生成器函数的暂停和恢复执行的能力,从而使得异步操作能够...

    Node.js连接OpenGauss异步转同步封装

    在Node.js中,异步编程是常态,但有时为了代码的简洁和易读性,开发者可能希望将异步操作转换为同步形式。这样做通常会借助于Promise或async/await语法。 在描述中提到,这个封装使得开发者可以在异步代码中直接...

    异步&同步加载树节点----zTree(一)

    首先,我们要理解什么是同步和异步加载。同步加载是指浏览器在执行代码时,如果遇到一个耗时的操作(如加载大量数据),会暂停后续操作,直到这个操作完成。而异步加载则不同,它允许浏览器在进行耗时操作的同时继续...

    Ajax异步与同步方法封装的js文件

    2 同步:MyAjaxs " 要调用的方法页面 方法" 参数 function d {alert d["d"] ;} ;">调用方法:1 将下载好的Ajax js文件引用到页面 代码如下:<script src " js MyAjax js" type &...

    同步执行和异步执行学习Demo

    在实际应用中,开发者常常结合同步和异步来设计程序。例如,Node.js的事件驱动模型就是一个很好的例子,它的单线程模型中,大部分I/O操作都是异步的,以提高服务器性能,但对于计算密集型任务,可能会引入线程池进行...

    浅谈js的ajax的异步和同步请求的问题

    在讲解JavaScript中的Ajax技术时,经常会遇到异步和同步请求的问题。本文旨在深入探讨这一问题,并通过实例代码说明在Ajax调用中如何控制这两种请求方式。 Ajax(Asynchronous JavaScript and XML)是一种在无需...

    关于解决jquery同步异步的问题

    下面是一段示例代码,它展示了如何在jQuery中实现异步和同步请求: ```javascript // JavaScript部分 function test() { var a = 1; $.ajax({ type: 'GET', url: 'test.php', data: 'page=112', success: ...

    JS同步、异步、延迟加载的方法

    本文讲述了JS同步、异步、延迟加载的方法。分享给大家供大家参考,具体如下: 一:同步加载 我们平时使用的最多的一种方式。 [removed][removed] 同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止后续的解析...

    Javascript异步编程(英文版)

    早期的JavaScript主要是单线程同步执行的,但随着Ajax技术的出现,异步请求成为可能,开启了JavaScript异步编程的新篇章。近年来,随着ES6及更高版本的引入,JavaScript社区不断完善异步编程模型,使得异步编程变得...

    AJAX同步或异步流程图

    首先,我们来看同步和异步这两种通信方式的区别: 1. **同步通信**:在同步模式下,浏览器会阻塞其他所有脚本执行,直到当前的AJAX请求完成。这意味着用户无法进行任何其他操作,直到请求返回结果。这种方式虽然...

    同步方法和异步方法比较

    在编程领域,同步和异步方法是处理任务执行方式的两种基本模式,它们在系统设计和性能优化中扮演着至关重要的角色。理解这两者的区别及其优缺点对于开发高效的应用至关重要。 **同步方法**指的是调用一个函数或方法...

    不同js异步函数同步的实现方法

    在复杂的应用场景中,可能需要考虑更加健壮的同步机制,比如使用第三方库或原生的JavaScript异步工具如`async/await`和`Promise.all`等来处理复杂的异步逻辑。 总之,虽然JavaScript的异步特性使得我们可以编写非...

    JavaScript异步编程g.pdf

    综上所述,本书《JavaScript异步编程》不仅是JavaScript开发人员的实用指南,也是理解和掌握异步编程思想的重要参考书籍。通过阅读本书,开发者可以学习到如何在实际工作中运用这些技术,编写出更加高效和响应迅速的...

    异步函数同步_Make_an_asynchronous_function_synchronous_javascript

    本文将探讨如何在JavaScript中实现异步到同步的转换,以及这种方法的优缺点。 ### 异步编程基础 JavaScript的异步编程主要基于回调函数、Promise、async/await等机制。这些方法在处理非阻塞任务时非常有效,但它们...

    js异步加载.docx

    JavaScript 异步加载是网页优化的关键技术之一,它允许脚本在不阻塞浏览器解析页面的情况下进行下载和执行。在传统的同步加载模式下,JavaScript 文件的加载会暂停HTML的解析,直到该脚本完全下载并执行完毕。这可能...

    javascript将异步校验表单改写为同步表单.docx

    ### JavaScript将异步表单校验改写为同步表单的知识点详解 #### 一、背景与需求 在Web开发中,表单是用户交互的重要组成部分。为了提高用户体验,开发者通常采用异步方式来处理表单校验,即在用户提交表单前,通过...

    Javscript高性能编程+Javascript异步编程

    "JavaScript高性能编程"和"JavaScript异步编程"是两个关键的JavaScript专题,对于提升应用程序的性能和用户体验至关重要。 一、JavaScript高性能编程 1. **优化代码执行效率**:了解JavaScript引擎的工作原理,如...

    微信小程序异步API同步化研究.zip

    本研究主要探讨如何将微信小程序中的异步API进行同步化处理,以提升代码的可读性、可维护性和执行效率。 1. 异步编程基础 - 异步编程:当调用一个异步函数时,它不会阻塞主线程,而是立即返回,让其他任务可以继续...

    《JavaScript异步编程》PDF版本下载.txt

    综上所述,《JavaScript异步编程》这本书详细介绍了JavaScript中异步编程的各种技术和最佳实践,包括但不限于回调函数、事件循环、Promise、async/await等核心概念和技术。通过学习本书,读者可以更好地理解和掌握...

Global site tag (gtag.js) - Google Analytics