【总结】JS中同步、异步、宏任务、微任务和EventLoop

同步和异步

想必大家都听说过JavaScript是单线程的,那么这门语言又怎么来的同步和异步的说法呢?这里就涉及到一个很重要的点,JavaScript虽然是单线程的,但浏览器有多个线程,对于一个单页面,不仅有线程负责处理JavaScript代码,还有负责网络请求的线程,负责监听用户操作的线程,这些东西处理好之后会进入任务队列(后文会详谈),以通知主线程,即执行JavaScript代码的那个线程,这里就体现了异步的特点。
JavaScript本身出于简单的考虑,并没有提供开启线程的api,所以说JavaScript是单线程的也没毛病,但切勿以为这就说明整个页面就只在走JavaScript这一条线程了,这是不现实的

宏任务和微任务

接着上文,所有任务分为同步和异步两种,同步的就是很快就能得到结果的主线程遇到同步任务时会立马执行,而不会拖延到之后,而异步的情况则比较复杂。
异步任务分为宏任务和微任务两种,微任务处理完毕后才会处理宏任务,而该异步任务被判定为宏还是微取决于官方的定义。常见分类如下:

任务 类型
script(整体代码) 宏任务
用户交互 宏任务
IO操作 宏任务
setTimeout 宏任务
setInterval 宏任务
Promise.then 微任务
catch、finally 微任务

任务队列

当主线程遇到异步任务时,后台会分配线程处理,并再处理完之后将其的回调函数放入任务队列。这就是任务队列的组成,注意到是异步任务的回调函数推入到任务队列的,而非异步任务本身。

EventLoop

有了上述概念,就能轻松理解EventLoop是怎么一回事了。
JavaScript引擎会不断轮询任务队列,将最先加入的宏任务取出,放入调用栈,然后开始执行其中代码,引擎会从上至下执行完所有同步代码,当遇到微任务时,会放到最后来执行,而遇到宏任务则会放到任务队列的最末尾等待执行。(这里只是简单的阐述了EventLoop的过程,具体实现会复杂一些)

参见下图:

Promise

我们知道,Promise的出现是为了拯救回调地狱这种情况。但其实在我看来其并未改变回调地狱的本质,大家都是发送Ajax请求,获得结果后,其回调函数会放在任务队列中等待执行。只不过之前我们是把所有之后的请求都写在第一个回调函数中,导致看起来像一个洋葱,层层嵌套,而Promise则把请求和回调函数分开来写,每一个then中可以再返回一个Promise,使之看起来像一条链子

//回调地狱
ajax(url, () => {
    
    
    //业务代码
    ajax(url1, () => {
    
    
        //业务代码
        ajax(url2, () => {
    
    
            //业务代码
        })
    })
})
//Promise版本
ajax(url)
    .then(res => {
    
    
        //业务代码
    })
    .then(res => {
    
    
        //业务代码
    })
    .then(res => {
    
    
    	//业务代码
    });

async和await

await其实就是Promise的一些封装,使用await会比使用Promise看起来更加美观,就像同步代码一样,如下:

//async、await版本
async function foo(){
    
    
	let res1 = await ajax1(url)
	let res2 = await ajax2(res2)
	return res2
}

await之前的代码可以理解为new Promise传入的代码,而之后的代码则是Promise.then中的回调。
看下面例子:

setTimeout(_=>{
    
    
    console.log('10')
},0)
async function foo(){
    
    
    console.log('2')
    let msg =  await new Promise((res,rej)=>{
    
    
        console.log('3')
        res('9')
        console.log('4')
    })
    console.log('6')
    await new Promise((resolve, reject) => {
    
    
        console.log('7')
        resolve('000')
        console.log('8')
    })
    return msg
}
console.log('1')
foo().then(data => console.log(data)).catch(err=> console.log(err))
console.log('5')

正确的输出语序就是按数字大小来输出的。

猜你喜欢

转载自blog.csdn.net/weixin_55658418/article/details/128822730