js中的同步和异步

单线程的概念

如果大家熟悉java,应该都知道,java是一门多线程语言,我们常常可以利用java的多线程处理各种各样的事,比如说文件上传,下载等,而JavaScript是否也可以支持多线程呢?

答案是否定的,JavaScript是一门单线程语言,因此,JavaScript在同一个时间只能做一件事,单线程意味着,如果在同个时间有多个任务的话,这些任务就需要进行排队,前一个任务执行完,才会执行下一个任务,比如说下面这段代码

// 同步代码
function fun1() {
  console.log(1);
}
function fun2() {
  console.log(2);
}
fun1();
fun2();

// 输出
2

很容易可以看出,输出会依次输入1,2,因为代码是从上到下依次执行,执行完fun1(),才继续执行fun2(),但是如果fun1()中的代码执行的是读取文件或者ajax操作,文件的读取和数据的获取都需要一定时间,难道我们需要完全等到fun1()执行完才能继续执行fun2()么?为了解决这个问题,后面我们会介绍同步和异步的概念

为什么javascript是单线程

其实,JavaScript的单线程,与它的用途是有很大关系,我们都知道,JavaScript作为浏览器的脚本语言,主要用来实现与用户的交互,利用JavaScript,我们可以实现对DOM的各种各样的操作,如果JavaScript是多线程的话,一个线程在一个DOM节点中增加内容,另一个线程要删除这个DOM节点,那么这个DOM节点究竟是要增加内容还是删除呢?这会带来很复杂的同步问题,因此,JavaScript是单线程的。


为什么会有同步和异步

因为JavaScript的单线程,因此同个时间只能处理同个任务,所有任务都需要排队,前一个任务执行完,才能继续执行下一个任务,但是,如果前一个任务的执行时间很长,比如文件的读取操作或ajax操作,后一个任务就不得不等着,拿ajax来说,当用户向后台获取大量的数据时,不得不等到所有数据都获取完毕才能进行下一步操作,用户只能在那里干等着,严重影响用户体验

因此,JavaScript在设计的时候,就已经考虑到这个问题,主线程可以完全不用等待文件的读取完毕或ajax的加载成功,可以先挂起处于等待中的任务,先运行排在后面的任务,等到文件的读取或ajax有了结果后,再回过头执行挂起的任务,因此,任务就可以分为同步任务和异步任务

同步任务

同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务。

异步任务

异步任务是指**不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,**当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务。

function fun1() {
  console.log(1);
}
function fun2() {
  console.log(2);
}
function fun3() {
  console.log(3);
}
fun1();
setTimeout(function(){
  fun2();
},0);
fun3();
 
// 输出
1
3
2

异步机制

那么,JavaScript中的异步是怎么实现的呢?那要需要说下回调和事件循环这两个概念啦

首先要先说下任务队列,我们在前面也介绍了,异步任务是不会进入主线程,而是会先进入任务队列,任务队列其实是一个先进先出的数据结构,也是一个事件队列,比如说文件读取操作,因为这是一个异步任务,因此该任务会被添加到任务队列中,等到IO完成后,就会在任务队列中添加一个事件,表示异步任务完成啦,可以进入执行栈啦~但是这时候呀,主线程不一定有空,当主线程处理完其它任务有空时,就会读取任务队列,读取里面有哪些事件,排在前面的事件会被优先进行处理,如果该任务指定了回调函数,那么主线程在处理该事件时,就会执行回调函数中的代码,也就是执行异步任务啦

单线程从从任务队列中读取任务是不断循环的,每次栈被清空后,都会在任务队列中读取新的任务,如果没有任务,就会等到,直到有新的任务,这就叫做任务循环,因为每个任务都是由一个事件触发的,因此也叫作事件循环

总的来说,JavaScript的异步机制包括以下几个步骤

  1. 所有同步任务都在主线程上执行,行成一个执行栈
  2. 主线程之外,还存在一个任务队列,只要异步任务有了结果,就会在任务队列中放置一个事件
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面还有哪些事件,那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行
  4. 主线程不断的重复上面的第三步
### JavaScript 同步异步编程的区别及工作原理 JavaScript 的同步异步编程模型是基于其单线程执行环境而设计的,这种设计使得代码执行顺序资源管理必须非常谨慎。理解同步异步的关键在于事件循环(Event Loop)机制以及任务队列(Task Queue)的作用。 #### 同步编程 在同步编程中,代码按照顺序依次执行。每条语句必须等待前一条语句执行完毕后才能开始执行。这种模式简单直观,但存在明显的缺点:如果某段代码执行时间较长,例如进行复杂的计算或等待 I/O 操作完成,主线程会被阻塞,导致整个程序无法响应用户交互或渲染更新。 例如: ```javascript console.log("Step 1"); console.log("Step 2"); ``` 上述代码会依次输出 "Step 1" "Step 2",没有任何延迟[^5]。 #### 异步编程 异步编程允许某些操作在后台运行,而不会阻塞主线程。常见的异步操作包括网络请求(如 `fetch`)、定时器(如 `setTimeout`、`setInterval`)以及文件读写等。JavaScript 使用回调函数、Promise 对象以及 `async/await` 语法来处理异步逻辑,从而实现非阻塞执行。 异步任务通常被放入任务队列中,当主线程空闲时,事件循环会从任务队列中取出下一个任务并执行。这种机制确保了即使有耗时操作,也不会影响其他代码的执行。 一个典型的异步示例是使用 `setTimeout`: ```javascript console.log("1. 开始"); setTimeout(() => { console.log("3. 异步任务完成,执行回调"); }, 2000); console.log("2. 继续执行主线程代码"); ``` 输出结果为: ``` 1. 开始 2. 继续执行主线程代码 3. 异步任务完成,执行回调 ``` 这说明异步任务不会阻塞主线程的执行流程。 #### 工作原理:事件循环与任务队列 JavaScript 引擎通过事件循环机制协调同步异步任务的执行。其核心流程如下: 1. **主线程执行同步任务**:所有同步代码按顺序执行。 2. **异步任务注册回调**:异步操作(如 `setTimeout`)将回调函数注册到任务队列中。 3. **事件循环检查任务队列**:当主线程为空时,事件循环从任务队列中取出最早的任务并执行其回调函数。 此外,现代 JavaScript 支持使用 `Promise` `async/await` 来更优雅地组织异步逻辑。`Promise` 提供了一种链式调用的方式,避免了“回调地狱”问题;而 `async/await` 则让异步代码看起来更像同步代码,提高了可读性可维护性[^2]。 #### 总结 - **同步编程**:代码顺序执行,容易理解但可能造成阻塞。 - **异步编程**:利用事件循环任务队列实现非阻塞执行,适用于耗时操作。 - **关键机制**:事件循环负责调度任务队列中的异步回调进入主线程执行。 JavaScript 的异步特性使其能够在单线程环境下高效处理并发任务,同时保持良好的用户体验。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值