同步与异步
javascript语言是一门“*单线程*”的语言
同步和异步的差别就在于这条流水线上各个流程的执行顺序不同。
最基础的异步是setTimeout和setInterval函数
console.log( "1" );
setTimeout(function() {
console.log( "2" )
}, 0 );
setTimeout(function() {
console.log( "3" )
}, 0 );
setTimeout(function() {
console.log( "4" )
}, 0 );
console.log( "5" );
// 1 5 2 3 4
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任(asynchronous)。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行。
具体来说,异步运行机制如下:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
宏任务和微任务
宏任务和微任务是等待任务队列中的异步任务的处理机制
浏览器的任务队列:
- 主任务队列存储的都是同步任务;
- 等待任务队列存储的都是异步任务;
首先浏览器会把主任务队列中的同步任务挨个全部执行完,然后再去等待任务队列中看哪个任务可以执行了,然后把该执行的任务放到主任务队列中去执行,等这个任务执行完,再去等待任务中看谁可以执行了,再把这个任务放到主任务队列中执行... 如此循环。这种循环叫做事件循环(Event Loop)
异步任务都是谁先到达条件谁先执行,但是谁先到达执行条件也有优先级的问题,这个优先级要看这个任务是宏任务还是微任务;微任务的优先级比宏任务的要高;
所以可以看做是这样的:浏览器线程先执行同步任务,途中遇到异步任务就将其加入到等待任务队列中去,然后继续向下执行,等同步任务全部执行完毕后,再去等待任务队列中去将所有可执行的微任务逐个执行,执行完后在拿取第一个先到达执行条件的宏任务来执行,执行完后再去等待任务队列中清理执行完所有已到达执行条件的微任务,然后再拿取下一个宏任务来执行,如果宏任务执行产生微任务或者微任务执行产生宏任务就一样加入到等待任务队列中,然后还是按照主线程每次到等待队列中先执行完所以的微任务再逐个执行宏任务的顺序来走
微任务:
-
Promise的then的回调函数
-
async 函数await下面的代码;
-
process.nextTick
宏任务:
-
定时器(setInterval和setTimeout)
案例
console.log(1);
Promise.resolve().then(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3);
Promise.resolve().then(() => {
console.log(4);
});
});
});
setTimeout(() => {
console.log(5);
})
Promise.resolve().then(() => {
console.log(6);
});
setTimeout(() => {
console.log(7);
})
console.log(8);
// 1 8 2 6 3 4 5 7
async function async1(){
console.log(1);
await async2()
console.log(2);
}
async function async2(){
console.log(3);
}
console.log(4);
setTimeout(()=>{
console.log(5);
},0)
async1();
new Promise((resolve)=>{
console.log(6);
resolve();
}).then(()=>{
console.log(7);
})
console.log(8);
//4 1 3 6 8 2 7 5
function a(){
return new Promise(((resolve,reject) =>{
console.log(4);
setTimeout(()=>{
console.log(7);
resolve(6);
},1000)
console.log(5);
}));
}
function b(result){
return new Promise(((resolve,reject)=>{
setTimeout(()=>{
console.log(result);
resolve(8);
},2000)
}))
}
async function f(){
let aP = await a();
console.log(1);
let bP = await b(aP);
console.log(bP);
console.log(2);
}
f();
console.log(9);
//4 5 9 7 1 6 8 2
注意:
await 后跟函数,会立即执行,但是会阻塞await后的语句
await后跟数值,会阻塞。
当js执行遇到ajax
js执行遇到ajax也会被放进等待任务队列中去,不过不同的是ajax不算是宏任务也不算是微任务,
ajax达到执行条件就会立即执行,发送请求嘛,网速快就执行的快网速慢就执行的慢。
Dom
操作到底是同步,还是异步?
js里面的Dom操作代码,是同步执行,但浏览器进行的Dom渲染,是异步操作。
浏览器渲染Dom和执行js,同时只能二选一,渲染一次Dom的时机是,当前宏任务和小尾巴微任务执行完,下一个宏任务开始前