这篇文章将为大家详细讲解有关javascript异步的作用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
我们知道JavaScript的单线程的,这与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
ajax的同步请求就会导致浏览器产生假死,因为它会锁定浏览器的UI(按钮,菜单,滚动条等),并阻塞所有用户的交互,jquery中的ajax有这样一个同步请求的功能,一定要慎用,尤其是在请求的数据量很大的时候,要避免使用同步请求。
举几个栗子感受一下异步
后台接口使用easy-mock,官方地址:https://easy-mock.com/
ajax使用axios,基本代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>javascript异步</title> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> </head> <body> <button>点击</button> <script> { let myData = null //ajax请求 function ajax() { axios.get('https://easy-mock.com/mock/5b0525349ae34e7a89352191/example/mock') .then(data => { console.log("ajax返回成功");// handle success myData = data.data console.log(myData); }) .catch(error => { // console.log(error); // handle error console.log("ajax返回失败"); }) } } </script> </body> </html>
我们通过添加一些js来看下效果,
console.log(myData); setTimeout(() => { console.log('定时器'); }, 2000); console.log(myData);
输出,应该没什么悬念
//null //null //定时器
执行顺序:
先执行第一个 console.log(myData);
然后遇到了定时器,将定时器挂起(就是暂停了这个定时器)
继续执行第二个 console.log(myData);
没有可以执行的js代码,回头把挂起的任务继续执行下去
继续看下一个栗子
console.log(myData); ajax() console.log(myData);
看下输出,依然没有悬念
//null //null //ajax返回成功 //{success: true, data: {…}}(这是接口返回的数据,我们不必关心返回的具体内容,只要知道返回了就好,陌上寒注)
执行顺序和上面的定时器基本类似,不在此赘述。
将两个栗子合并,我们看下
console.log(myData); ajax() setTimeout(() => { console.log('定时器'); }, 2000); console.log(myData);
输出,
//null //null //ajax返回成功 //{success: true, data: {…}} //定时器
发现问题了吗?两个异步函数相遇了,先执行谁?谁跑的快就先执行谁?
也可以这么说,其实这引发了另外一个知识点,
两个 console.log(myData);是同步执行的,他们都在js的主线程上执行,
在主线程之外还存在一个任务队列,任务队列中存放着需要异步执行的内容
当主线程运行完毕之后,就会去执行任务队列中的任务(不断的重复扫描)直到任务队列清空
观察这段代码
console.log(1); setTimeout(function () { console.log(2); }, 1000); console.log(3);
输出:1,3,2,这没什么可解释的
再看一段代码
setTimeout(function(){console.log(1);}, 0); console.log(2);
输出:2,1,为什么会这样?
console.log(2);在主线程中,先执行,
setTimeout(function(){console.log(1);}, 0);放在了任务队列中,
只有在主线程执行完了才会去执行任务列队中的内容
看这段代码,有助于你的理解
console.log(myData); ajax() setTimeout(() => { console.log('定时器'); }, 2000); console.log(myData); const btn = document.querySelector('button') btn.onclick = () => { console.log("点击了"); }
我们为button按钮添加了点击事件,在浏览器刷新的同时不停地对按钮进行点击操作(当然是手动点击)
看下输出:
//null //null //(10次输出)点击了 //ajax返回成功 //{success: true, data: {…}} //定时器 //点击了
这样是不是可以理解为什么主线程要去循环扫描任务列队了?
事件循环的每一轮称为一个tick(有没有联想到vue中的nextTick?)
当产生用户交互(鼠标点击事件,页面滚动事件,窗口大小变化事件等等),ajax,定时器,计时器等,会向事件循环中的任务队列添加事件,然后等待执行,
定时任务:setTimeout,setInverval
网络请求:ajax请求,img图片的动态加载
事件绑定或者叫DOM事件,比如一个点击事件,我不知道它什么时候点,但是在它点击之前,我该干什么还是干什么。用addEventListener注册一个类型的事件的时候,浏览器会有一个单独的模块去接收这个东西,当事件被触发的时候,浏览器的某个模块,会把相应的函数扔到异步队列中,如果现在执行栈中是空的,就会直接执行这个函数。
ES6中的Promise
在可能发生等待的情况
等待过程中不能像alert一样阻塞程序的时候
因此,所有的“等待的情况”都需要异步
一句话总结就是需要等待但是又不能阻塞程序的时候需要使用异步
千万不要把异步和并行搞混了,
异步是单线程的,并行是多线程的
异步:主线程的任务以同步的方式执行完毕,才会去依次执行任务列队中的异步任务
并行:两个或多个事件链随时间发展交替执行,以至于从更高的层次来看,就像是同时在运行(尽管在任意时刻只处理一个事件)
关于javascript异步的作用就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。