理解js中的同步和异步
首先要先了解下js单线程
一、为什么js是单线程?
其实,JavaScript的单线程,与它的用途是有很大关系,我们都知道,JavaScript作为浏览器的脚本语言,主要用来实现与用户的交互,利用JavaScript,我们可以实现对DOM的各种各样的操作,如果JavaScript是多线程的话,一个线程在一个DOM节点中增加内容,另一个线程要删除这个DOM节点,那么这个DOM节点究竟是要增加内容还是删除呢?这会带来很复杂的同步问题,因此,JavaScript是单线程的
二、同步任务和异步任务
(1)为什么会有同步和异步?
因为JavaScript的单线程,因此同个时间只能处理同个任务,所有任务都需要排队,前一个任务执行完,才能继续执行下一个任务,但是,如果前一个任务的执行时间很长,比如文件的读取操作或ajax操作,后一个任务就不得不等着,拿ajax来说,当用户向后台获取大量的数据时,不得不等到所有数据都获取完毕才能进行下一步操作,用户只能在那里干等着,严重影响用户体验。
因此,JavaScript在设计的时候,就已经考虑到这个问题,主线程可以完全不用等待文件的读取完毕或ajax的加载成功,可以先挂起处于等待中的任务,先运行排在后面的任务,等到文件的读取或ajax有了结果后,再回过头执行挂起的任务,因此,任务就可以分为同步任务和异步任务。
其实同步和异步,
无论如何,做事情的时候都是只有一条流水线(单线程),
同步和异步的差别就在于这条流水线上各个流程的执行顺序不同。
(2) 同步任务
同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务
,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务。
(3) 异步任务
异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程
,当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务
<script>setTimeout(function(){console.log(3);},5000)console.log(1);console.log(2);</script>
执行顺序是这样的,先输出1和2等待5秒后输出3
上述代码具体的执行过程是:
1. 先执行执行栈中的同步任务
2. 遇到异步任务(回调函数)就放入任务队列中
3. 一旦执行栈中的同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,被读取的异步任务结束等待状态,进入执行栈开始执行。
(4) 异步机制
那么,JavaScript中的异步是怎么实现的呢?那要需要说下回调和事件循环
这两个概念啦
首先要先说下任务队列,前面也介绍了,异步任务是不会进入主线程,而是会先进入任务队列
,任务队列其实是一个先进先出
的数据结构,也是一个事件队列,比如说文件读取操作,因为这是一个异步任务,因此该任务会被添加到任务队列中,等到IO完成后,就会在任务队列中添加一个事件
,表示异步任务完成啦,可以进入执行栈啦~ 但是这时候,主线程不一定有空,当主线程处理完其它任务有空时,就会读取任务队列,读取里面有哪些事件,排在前面的事件会被优先进行处理,如果该任务指定了回调函数
,那么主线程在处理该事件时,就会执行回调函数中的代码,也就是执行异步任务啦
单线程从任务队列中读取任务是不断循环的,每次执行栈被清空后,都会在任务队列中读取新的任务,如果没有任务,就会等待,直到有新的任务,这就叫做任务循环,因为每个任务都是由一个事件触发的,因此也叫做事件循环
总的来说,JavaScript的异步机制包括以下几个步骤
1. 所有同步任务都在主线程上执行,形成一个执行栈。
2. 主线程之外,还存在一个任务队列,只要异步任务有了结果,就会在任务队列中放置一个事件
3. 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,先执行微任务队列再执行宏任务队列。
4. 主线程不断的重复上面的第三步
三、js常见的异步操作(个人理解需要耗时很长的操作)
1) setTimeout (setInterval)
2)AJAX
3)事件绑定(如:.on,.bind,.listen,.addEventListener,.observe)
4)观察者模式(例如:websocket中就有发布和订阅之类的操作)
5)promise 是一个异步操作,但是它是一个异步操作的解决方案,即保存着异步操作的结果,可以把异步函数以同步函数的形式写出来(Promise的成功|失败的回调(.then()/.catch())
6)async/await — 使用generator的语法糖,可以理解为是generator的一种改进
7)generator函数 — 使用promise的语法糖,可以理解为是promise的另一种拓展
三、 异步操作以同步的方式去执行
为了解决异步编程,出现了三种类似的用于解决异步操作的方案。(个人理解这种方式为了解决异步操作的,都是异步操作变成同步;需要取到上个方法返回的值,才能继续正常往下执行,见下方回调函数具体需求的描述)
1)回调函数(回调函数就是将一个函数当作另一个主函数的参数来使用的函数。)
这是异步编程最基本的方法
需求:
假定有两个函数 test1 和 test2,后者等待前者的执行结果。
如果test1()是一个比较耗时的任务,就会把test1放入任务队列中,先执行test2的代码,这样test2需要的变量会报错undfind;所以可以考虑改写test1(),把test2()写成test1()的回调函数
改写如下:
function test1(callback){ //(主函数)console.log('执行了test1'); //主函数任务代码setTimeout(function () {callback();}, 1000);
}
function test2(){//回调函数console.log('执行了test2');
}test1(test2); // 执行
解读:
回调函数是传统的一种异步编程解决方案,其原理就是将一个函数当作参数传递到另一个主函数中,当主函数执行完自身的内容之后,在运行传递进来的回调函数。
采用这种方式,我们把同步操作变成了异步操作,test1()不会堵塞程序运行,相当于先执行主程序的主要逻辑,将耗时的操作推迟执行。
优点:
简单,容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱
缺点:
一个任务只能有一个回调函数
2)promise
已经知道setTimeout是一种异步操作了,因此这里的例子可以将每一个setTimeout拟作一次接口请求
需求:
存在两个函数a(), b(), 需要在a() 函数所有内容执行完毕之后,再执行b()函数;但是a()函数又存在异步操作(获取数据);
如果按顺序执行a(),b(),就会导致b()先执行,出现数据内容不存在的情况(a是异步的)(可能想表达的是b函数依赖a函数的某些数据吧);
因此使用promise来处理
自我理解:
因为a()函数里有异步操作,就会放入任务队列,等同步操作执行完毕再从任务队列进入主线程执行;
现在想按照 a() =>b() 顺序执行;所以需要promise,通过resolve获取成功的值之后,结果作为.then的返回值,再去调用b()
先不用.then()处理异步任务
用.then() 处理异步任务
这样就达成了理想的效果,先执行a()函数,然后执行b()函数。
3)async-await的基础用法
3.1 async
函数和普通函数一样执行,是generator的语法糖。
3.2 async
函数的返回值是Promise 对象
,所以可以用.then方法指定下一步的操作,return语句返回的值,会成为then方法回调函数的参数
async function test() {return 'hello world'
}
test().then(result => {console.log(result);
})
3.3 基本使用示例:多个异步操作完成后才会进行后续操作
await后面的函数建议 返回 Promise对象(即return new Promise((resolve,reject)=>{})) 并且主动调用resolve()才能够进行后续的then或者是后续的await操作,倘若是执行了reject(reject的参数会被catch方法的回调函数接收到)或者throw抛出错误之类的就会导致当前执行中断。
注意1:await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在
try…catch
代码块中。
注意2:多个await命令后面的异步操作,如果不存在先后关联
,最好让它们同时触发
。不然会增加耗时;
这样做的好处就是,如果两个await直接运行则需要2秒的时间才会运行后续的内容,
但是像这样处理一下,两个就会同时开始,即只需要1秒就可以运行后续的内容了。
上一篇: 理解异步函数async和await的用法
理解js中的同步和异步相关推荐
- 如何理解js中的同步和异步
首先需要理解:JS是单线程运行的 同步和异步,无论如何,做事情的时候都是只有一条流水线(单线程),同步和异步的差别就在于这条流水线上各个流程的执行顺序不同. 同步就是程序按照正常的执行顺序,依次执行 ...
- JS笔记(20): JS中的同步编程和异步编程
铺垫:关于定时器 定时器:设定一个定时器,并且设定了等到的时间,当到达指定的时间,浏览器会把对应的方法执行 1)常用的定时器 1.setTimeout(function,intarval) 执行一次 ...
- 【JavaScript】JS执行机制--同步与异步
目录 单线程 同步与异步 事件循环 单线程 JavaScript语言具有单线程的特点,同一个时间只能做一件事情.这是因为JavaScript脚本语言是为了处理页面中用户的交互,以及操作DOM而诞生的. ...
- 彻底理解js中this
相关博文:http://blog.csdn.net/libin_1/article/details/49996815 彻底理解js中this的指向,不必硬背. 首先必须要说的是,this的指向在函数定 ...
- 理解js中this的指向
彻底理解js中this的指向 JavaScript 的 this 指向问题深度解析 转载于:https://www.cnblogs.com/jeacy/p/6509616.html
- 深入理解Js中的this
深入理解Js中的this JavaScript作用域为静态作用域static scope,但是在Js中的this却是一个例外,this的指向问题就类似于动态作用域,其并不关心函数和作用域是如何声明以及 ...
- js原型和原型链_理解JS中的原型和原型链
导读:JavaScript中(JS)的原型和原型链是web前端开发面试中经常被问到的问题:同时,如果我们能很好的理解JS中的原型和原型链,对于控制台输出的很多信息我们也能更好的理解,而原型链也是实现继 ...
- 理解JS中的声明式与命令式
理解JS中的声明式与命令式? 声明式编程 :告诉机器你想要的是什么 让机器想出去做 优缺点 : 声明式减少了可变量(Immutable Variable)的声明,程序更为安全, 代码更加简洁 ...
- 理解js中的面向对象
目录 前言: 一点疑问: 1.封装 2.继承 原型链的查找机制 不容易理解的点: ----重点在最后---- 前言: js是一门面向对象的语言,但是又没有类的概念,虽然后来加入了class,但也就是个 ...
最新文章
- “重”磅!人造物质量首超全球生物量
- boostrap-table export 导出监听
- mysql把游标数据存入表中_利用Python爬股票数据并存入数据库Mysql
- iPhone新机或全部采用OLED屏:日本JDI股价应声下跌
- libcurl 遇到的问题
- Flutter 升级 2.0 填坑指导,带你原地起飞
- 把握linux内核设计思想系列【转】
- C语言之选择结构与循环结构
- mysql 向量写法_mysql – 你如何在Ruby中处理一个非常大的向量?
- ssh配置config文件
- chrome 迅雷下载 支持插件, 让chrome支持迅雷
- JAVA端收集Liunx服务器 CPU 内存 磁盘使用率
- webstorm官网中文破解版(转自http://blog.csdn.net/vchen_hao/article/details/77248053)
- 计算机快捷键ctrl记忆,PS篇:有效记忆快捷键
- 兼容性问题:安卓正常,ios报错invalid group specifier name
- 帮你分清嵌入式与单片机
- 如何用MD5加密数据库的敏感数据?
- 深入学习理解Java集合
- 数字世界的积木-从MOS管搭反相器,与非门,锁存器,触发器
- cad怎么倒圆角_15个超实用CAD技巧,效率递增10倍,设计院师傅都在用