共计 3470 个字符,预计需要花费 9 分钟才能阅读完成。
从今天开始研究一下 javascript 的异步相关内容,感兴趣的请关注
同期异步系列文章推荐 javascript 异步中的回调 javascript 异步与 promisejavascript 异步之 Promise.all()、Promise.race()、Promise.finally()javascript 异步之 Promise.resolve()、Promise.reject()javascript 异步之 Promise then 和 catchjavascript 异步之 async(一)javascript 异步之 async(二)javascript 异步实战 javascript 异步总结归档
什么是 js 异步?
我们知道 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 代码,回头把挂起的任务继续执行下去继续看下一个栗子
异步 -ajax
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 一样阻塞程序的时候
因此,所有的“等待的情况”都需要异步
一句话总结就是需要等待但是又不能阻塞程序的时候需要使用异步
异步和并行
千万不要把异步和并行搞混了,异步是单线程的,并行是多线程的异步:主线程的任务以同步的方式执行完毕,才会去依次执行任务列队中的异步任务并行:两个或多个事件链随时间发展交替执行,以至于从更高的层次来看,就像是同时在运行(尽管在任意时刻只处理一个事件)
原文链接
参考链接关于 js 中的同步和异步异步和单线程——什么时候需要异步,前端使用异步的场景 Javascript 异步编程的 4 种方法