微任务、宏任务与Event-Loop
setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3')
});
console.log('4');
请你给出这段代码的运行顺序。
同步异步我看过很多的讲解,大多都是要么你就一个setTimeout函数,要么就一个Promise函数。两个函数放到一起的我还真没见过。于是我就想:
settimeout肯定是异步的。 我也知道有一个event事件队列,你setTimeout没设置时间应该直接就进入这个队列了吧,然后就是Promise的回掉函数进入event队列。 答案应该是 2,4,1,3.
其实不然
同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table事件表并注册函数。当指定的事情完成时,Event Table时间表会将这个函数移入事件队列Event Queue。主线程内的任务执行完毕为空,会去Event Queue事件队列读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)。
但是js异步有一个机制,就是遇到宏任务,先执行宏任务,将宏任务放入事件队列event queue,然后在执行微任务,将微任务放入事件队列event queue
最骚的是,这两个事件不放在同一个队列 (这个queue不是一个queue)。当你往外拿的时候先从微任务事件队列里拿这个回调函数,然后再从宏任务的事件队列queue上拿宏任务的回调函数。
再盗个图
宏任务一般是:包括整体代码script,setTimeout,setInterval。
微任务:Promise,process.nextTick。
记住就行了。然后回到开头代码。
因为settimeout是宏任务,
虽然先执行的,但是他被放到了宏任务的event queue里面,
然后代码继续往下检查看有没有微任务,检测到Promise的then函数把他放入了微任务序列。
等到主线进程的所有代码执行结束后。
先从微任务queue里拿回调函数,然后微任务事件队列queue空了后再从宏任务的事件队列queue拿函数。
所以正确的执行结果当然是:2,4,3,1。
同步和异步的区别
同步,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令。
异步,执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。
同步,就是实时处理(如打电话),比如服务器一接收客户端请求,马上响应,这样客户端可以在最短的时间内得到结果,但是如果多个客户端,或者一个客户端发出的请求很频繁,服务器无法同步处理,就会造成涌塞。
同步如打电话,通信双方不能断(我们是同时进行,同步),你一句我一句,这样的好处是,对方想表达的信息我马上能收到,但是,我在打着电话,我无法做别的事情。
异步,就是分时处理(如收发短信),服务器接收到客户端请求后并不是立即处理,而是等待服务器比较空闲的时候加以处理,可以避免涌塞。
异步如收发收短信,对比打电话,打电话我一定要在电话的旁边听着,保证双方都在线,而收发短信,对方不用保证此刻我一定在手机旁,同时,我也不用时刻留意手机有没有来短信。这样的话,我看着视频,然后来了短信,我就处理短信(也可以不处理),接着再看视频。
对于写程序,同步往往会阻塞,没有数据过来,我就等着,异步则不会阻塞,没数据来我干别的事,有数据来去处理这些数据。
同步在一定程度上可以看做是单线程,这个线程请求一个方法后就待这个方法给他回复,否则他不往下执行(死心眼)。
异步在一定程度上可以看做是多线程的(废话,一个线程怎么叫异步),请求一个方法后,就不管了,继续执行其他的方法。