什么是异步编程
同步编程:一件事情做完之后才能去做另外一件事情(单线程)
异步编程:上一件事情没有处理完的情况下,就可以去做下一件事情(多线程)
JS 代码在浏览器中执行,而浏览器是多进程的,当我们打开一个界面的时候就相当于开启了一个进程,在这一个进程里会存在多个线程(JS引擎、GUI渲染、HTTP网络请求、DOM事件监听器、定时器监听线程…),其中JS引擎就来执行JS代码,很明显它是一个单线程,因为它是单线程所以默认情况下它只能同步完成代码执行,但是JS可以实现异步,这是因为它内部存在事件循环和事件队列机制。
同步和异步
单线程
Javascript语言的执行环境是”单线程”(single thread)。
所谓”单线程”,就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推
同步
“同步模式”就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的
生活中的同步是指同一时间只能处理一件事情
异步
“异步模式”则是每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的
事件循环
所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个执行任务。定时器
setTimeout
是时间为0 后才添加到任务队列里一旦”执行栈”中的所有同步任务执行完毕,事件循环系统就会读取”任务队列”,看看里面有哪些对应的异步任务,按照于是结束等待状态,进入执行栈,开始执行。
主线程不断重复上面的第三步
根据规范:事件循环是通过 「任务队列」 的机制来进行协调的。一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),「源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列」。
setTimeout/Promise
等API便是任务源,而进入任务队列的是他们指定的具体执行任务
异步任务分类
宏任务macrotask
发起者:宿主(Node、浏览器)
事件:script(整体代码)、setTimeout、setInterval、setImmediate(Node.js 环境)、UI事件、I/O(Node.js)
运行:后运行
微任务microtask
发起者: JS引擎
事件:Promise、MutaionObserver、process.nextTick(Node.js)
运行:先运行
微任务优先级高于宏任务,先执行完所有微任务,宏任务在之后执行
关系图
示例动画
回调函数
回调函数是一个函数,将会在另一个函数完成执行后立即执行。回调函数是一个作为参数传给另一个 JavaScript 函数的函数。这个回调函数会在传给的函数内部执行。
- 常见的回调函数:ajax的success/定时器的函数/jquery动画里的函数
- 作用:进行后续操作,起到一个通知的作用
- 应用:
- 定时器(异步回调)
- ajax(异步回调)
- 事件处理程序(异步回调)
- sort
分类
- 根据是否是同步操作还是异步的操作,将回调函数分为同步回调和异步回调两种
同步回调
- 回调函数的代码必定是按照书写顺序来执行的
- 立即执行,完全执行完才结束,不会放入回调队列中
- 例子:数组遍历相关的回调函数
function task1(callback){
console.log('任务1执行中');
callback();
}
function task2(){
console.log('任务2执行中');
}
task1(task2);
异步回调
- 回调函数的执行跟书写顺序不一致。
- 不会立即执行,会放入回调队列中将来执行
- 例子:定时器回调、ajax回调、promise的成功失败回调
setInterval(() => {
console.log('1000后执行');
}, 1000);
console.log('定时器之后的代码,会在回调函数之前执行');
回调地狱
背景
- 实际应用中,很多时候,需要多个异步操作能够按照顺序执行。比如先获取电影数据的个数,获取数据的个数后再获得具体的电影数据,还需要获取电影类型的数据。这样导致了回调函数的不断嵌套
概述
- 回调地狱是指因为多个异步操作需要按一定顺序执行,导致每个异步操作的回调函数代码形成了多层嵌套。导致了代码阅读不易,维护不方便
// 获取电影个数:第一个异步操作
$.ajax({
url:"地址",
success:function(data){
//拿到个数,根据个数在页面上生成对应数量的标签
//拿到具体的数据:第二个异步操作
$.ajax({
url:"地址",
success:function(data){
//拿到电影数据
//拿到分类数据:第三个异步操作
$.ajax({
url:'',
success:function(){
}
})
}
})
}
});
解决办法:promise, async&await 处理有异步代码顺序执行的问题