前言:

javascript是一种单线程编程语言, 一般来说它的执行顺序是按照从上到下执行,但是有些特殊情况则会改变这样的执行顺序,我们需要理解和掌握其中的原理,需要了解同步任务和异步任务以及宏任务和微任务,这样才有助于我们在今后开发中及时发现代码执行问题,提高开发效率。Javascript 是单线程语言,所以一切 Javascript “多线程” 都是单线程模拟出来的。

1.Javascript 中的同步任务和异步任务

同步任务:可以立即执行不需要等待,例如 var a = 123

异步任务:不能立即执行,需要等待一段时间后才能执行,例如 xhr网络请求 和 setTimeout定时器。

执行流程如下:

执行整个脚本,解析到同步和异步任务时分别放进不同的执行场所,同步任务放进主线程,异步任务放进事件表(Event Table)中并注册函数。

当指定的事情执行完成时,事件表(Event Table)会将这个函数移入事件队列(Event Queue)中等待执行。

主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。

不断重复整个过程,形成Event Loop(事件循环)。

举一个例子:

console.log('111') //同步任务 1

setTimeout(()=>{ //异步任务 1

console.log('222')

},3000)

setTimeout(() => { // 异步任务 2

console.log('333')

}, 1000)

console.log('444') //同步任务 2

答:111 444 333 222

对这个过程进行解析:

将同步任务1和2 放入主线程中,将异步任务1和2放入Event Table,并注册setTimeout的回调函数。

执行 主线程中的任务 按照从上到下的先后顺序 先输出 111 后 输出 444

当setTimeout定时结束后返回回调函数进入 Event Queue等待执行。

主线程执行完毕后从Event Queue读取setTimeout的回调函数放入主线程中并执行,因为两个异步任务执setTimeout执行时间不同,所以返回回调函数先后顺序也有所不同。

2.为什么异步任务中要设置队列(事件监听器)?

对于http、xhr等网络请求,执行的时间是不确定的,针对于这种情况设置了消息队列(事件监听器)。

事件监听器可以监听异步任务的状态,如果可以执行回调就会将相应的任务放入事件队列中。

举个例子:

假设两个网络请求,监听器会先监听到第二个请求得到响应,那么会先执行第二个的回调,所以下面这段代码的输出是1 2 3 4

console.log('111') // 同步任务1

ajax().then(() => { // 异步任务1

console.log('222')

})

ajax().then(() => { // 异步任务2

console.log('333')

})

console.log('444') // 同步任务2

发起请求的两个异步任务,通过事件监听器来监听,如果异步任务2先返回回调函数,异步任务1后返回回调函数,那么就先执行异步任务1后执行异步任务2,答案则是:111 444 333 222。

3.什么是宏任务和微任务?

如果javascript 按照真正的单线程执行的话,如果在遇到任务繁多的情况下,那么执行效率会降低,执行的时间也会更长,或者在解析到中间代码过程中遇到了错误的代码,那么就会停止在这一步影响后面代码的执行,这是非常不好的用户体验。为了解决这种传统意义上的单线程执行问题,在需要执行很多任务的这种情况下,就需要利用微任务和宏任务模拟产生“多线程”才能确保代码更高的执行效率。

宏任务和微任务是异步任务中的两个分类。

在ES6规范中 宏任务是由宿主发起的,微任务是由Javascript自身发起的。

4.宏任务和微任务区别是什么?

宏任务:由宿主(Node、浏览器等)发起,在微任务后运行,并会触发新一轮的 netxtTick()。

具体事件:

script (可以理解为外层同步代码)

setTimeout(定时器) / setInterval(计数器)

UI rendering / UI事件

postMessage,MessageChannel5. setImmediate,I/O(Node.js)

微任务:由JS引擎发起,在宏任务前运行,不会触发新一轮的 netxtTick()。

具体事件:

Promise

MutaionObserver

Object.observe(已废弃;Proxy 对象替代)

process.nextTick(Node.js)

5.宏任务和微任务执行顺序

执行顺序:

执行脚本,先执行同步代码,遇到异步宏任务则放入到宏任务队列中,遇到异步微任务则放入到微任务队列中,当所有同步代码执行完毕后,再将异步任务从队列中调入主线程执行,先将微任务执行完毕后再将宏任务从队列中调入主线程执行。

异步任务有宏任务和微任务两种,先将宏任务添加到宏任务队列中,将宏任务里面的微任务添加到微任务队列中,所有同步任务执行完后执行异步任务,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将宏任务从队列中调入主线程执行。这个过程一直循环执行(事件循环 Event loop)

6.案例题

例题一:

const promise1=new Promise((resolve, reject) => {

console.log('promise1')

})

console.log('1', promise1);

// 'promise1' '1' Promise{}

解析:

从上到下,先执行到 new Promise函数, 实例化过程中执行的代码是同步任务 输出:'promise1'。

人后执行到同步代码 console.log('1', promise1); 这个时候的promise1 没有被 resolve 或者。reject,因此状态函数pending,输出:'1' Promise{}。

例题二:

const promise =new Promise((resolve, reject) => {

console.log(1);

resolve('success')

console.log(2);

});

promise.then(() => {

console.log(3);

});

console.log(4);

解析:

从上至下,先遇到new Promise,执行其中的同步代码1。

再遇到resolve('success'), 将promise的状态改为了resolved并且将值保存下来。

继续执行同步代码2。

执行到promise.then这个微任务,将其加入微任务队列。

执行同步代码4。

本轮宏任务全部执行完毕,检查微任务队列,发现promise.then这个微任务且状态为resolved,执行它。

例题三:

setTimeout(function(){

console.log('1');

});

new Promise(function(resolve){

console.log('2');

resolve();

}).then(function(){

console.log('3');

}).then(function(){

console.log('4')

});

console.log('5');

// 2 5 3 4 1

解析:

从上到下先执行到setTimeout,它是异步宏任务,放入到宏任务队列中。

执行到 new Promise 实例化过程中执行的代码是同步任务,放入主线程中执行,直接输出2。

promise.then中的回调函数是异步微任务,放入到微任务队列。

执行到 同步任务 console.log('5'); 放入主线程中执行,直接输出5,至此同步任务中的同步任务执行完成。

从微任务队列中取出微任务到主线程中,输出3、 4,微任务队列为空,然后检查宏任务队列。

从宏任务队列中取出宏任务到主线程中,输出1,宏任务队列为空。

例题四:

setTimeout(()=>{

new Promise(resolve=>{

resolve();

}).then(()=>{

console.log('test');

});

console.log(4);

});

new Promise(resolve=> {

resolve();

console.log(1)

}).then( () => {

console.log(3);

Promise.resolve().then(() => {

console.log('哈哈');

}).then(() => {

Promise.resolve().then(() => {

console.log('呵呵')

})

})

})

console.log(2);

// 1 2 3 哈哈 呵呵 4 test

解析:

从上到下执行到setTimeout,是异步宏任务,将setTimeout的回调函数放入宏任务队列。

遇到new Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出1。

而Promise.then中注册的回调才是异步执行的,将其放入微任务队列中。

遇到同步任务console.log(2),输出2;主线程中同步任务执行完。

从微任务队列中取出任务到主线程中,输出3,此微任务中又有微任务,Promise.resolve().then(微任务a).then(微任务b),将其依次放入微任务队列中。

从微任务队列中取出任务a到主线程中,输出 哈哈。

从微任务队列中取出任务b到主线程中,任务b又注册了一个微任务c,放入微任务队列中。

从微任务队列中取出任务c到主线程中,输出 呵呵;微任务队列为空。

从宏任务队列中取出任务到主线程,此任务中注册了一个微任务d,将其放入微任务队列中,接下来遇到输出4,宏任务队列为空。

从微任务队列中取出任务d到主线程 ,输出test,微任务队列为空。

好文推荐

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: