我们在前端开发工作中 经常会听到同步和异步两个词
1. 首先要了解同步和异步具体是什么意思?
在JavaScript中,同步和异步是两种不同的编程模型,用于控制代码的执行方式和顺序。
同步代码:是按照代码的顺序一步一步执行的,每一条语句必须等待上一条语句执行完成才能继续执行下一条语句。在执行一个同步代码时,如果遇到一个耗时的操作(比如网络请求、文件读取等),代码将会阻塞等待操作完成后再执行下一条语句。这种方式可能会导致页面冻结,用户体验不佳。 示例代码:
console.log("开始执行");
// 模拟一个耗时操作,阻塞代码执行
for (let i = 0; i < 100000000; i++) {}
console.log("耗时操作完成");
console.log("继续执行");
// 开始执行
// 耗时操作完成
// 继续执行
异步代码:则是不会等待上一条语句执行完成才继续执行下一条语句。当代码执行到一个异步操作时,会先将该操作加入任务队列,然后继续执行下一条语句,等待任务队列通知该操作完成后再执行相应的回调函数。在等待异步操作完成的过程中,代码不会被阻塞,可以继续执行其他操作,从而提高了程序的效率和用户体验。
JavaScript中常用的异步操作包括定时器(setTimeout和setInterval)、事件(比如鼠标点击、键盘敲击等)、Ajax请求和Promise等。在异步代码中,可以使用回调函数、Promise或async/await来处理异步操作的结果。
示例代码:
console.log("开始执行");
// 定义一个异步操作
setTimeout(function() {
console.log("异步操作完成");
}, 1000);
console.log("继续执行");
// 开始执行
// 继续执行
// 异步操作完成
2.任务队列是什么?
在上面的异步代码中提到了任务队列这个概念,并没有解释清楚
任务队列:是JavaScript中用来存放待执行的任务的一种数据结构。每当一个异步任务完成时,该任务会被推入任务队列中等待执行。JavaScript引擎会不断地从任务队列中取出任务并执行,直到队列为空。
简单说就是:脱离同步代码的执行路线,单独开个空间用于暂存待执行具有耗时的任务,避免阻塞同步代码的执行
3.任务队列类型有哪些
任务队列通常有两种类型:宏任务队列和微任务队列。执行顺序:微任务队列>宏任务队列
微任务队列: (微任务队列中的任务通常是当前任务执行完后需要立即执行的任务)通常只有一个Promise队列,可以使用Promise.resolve()、Promise.reject()、Promise.then()等方法将任务添加到微任务队列中
宏任务队列: (宏任务队列中的任务则可能需要等待较长时间)通常包括事件队列、定时器队列、UI渲染队列等
任务队列的执行顺序示例代码:
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
// script start
// script end
// promise1
// promise2
// setTimeout
代码执行顺序为:
- 执行console.log('script start'),输出 "script start"。
- 执行setTimeout(),将异步任务加入宏任务队列中。
- 执行Promise.resolve().then(),将两个微任务添加到微任务队列中。
- 执行console.log('script end'),输出 "script end"。
- 执行微任务队列中的第一个任务,即console.log('promise1'),输出 "promise1"。
- 执行微任务队列中的第二个任务,即console.log('promise2'),输出 "promise2"。
- 执行宏任务队列中的第一个任务,即setTimeout()的回调函数,输出 "setTimeout"。
4.任务队列的任务执行时间是什么时候呢?
JavaScript引擎会不断地从任务队列中取出任务并执行,直到队列为空
答案:微任务和宏任务的执行顺序和时间是由它们被添加到任务队列中的时间和优先级决定的,而不是必须在同步代码执行完毕后才开始执行。如果在同步代码执行期间有微任务或宏任务被添加到任务队列中,它们会在合适的时机被执行,不需要等待同步代码执行完毕。
这是因为:当同步代码执行完毕后,JavaScript引擎会首先执行所有微任务队列中的任务,然后再去执行宏任务队列中的任务。如果微任务队列中一直有任务等待执行,那么它们会一直被执行直到微任务队列为空,这个过程中可能不会有宏任务执行。当微任务队列为空时,JavaScript引擎才会检查宏任务队列,如果宏任务队列中有任务待执行,那么它们会被执行。
示例代码:
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
// A D C B
输出的结果为:A D C B,
因为微任务 C 的优先级高于宏任务 B,所以先输出 C,然后输出 B。而 B 的回调函数是在同步代码执行完毕后添加到宏任务队列中的,但它仍然在微任务 C 之后执行。
完~