js中的同步与异步执行顺序
放在前面
异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task
queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
注:setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。
通用公式
同步>异步>回调
举个例子
for (var i = 0; i < 9; i++) {
setTimeout( function(){
console.log(i);
}
,1000)
}
console.log(i);
先执行同步再执行异步最后执行回调,for循环先执行,每个i都将setTimeOut回调扔到消息队列中,接着执行后面的同步console.log,此时i=9,到此同步执行完毕,回消息队列执行回调,9个setTimeOut依次执行,999999999.
let a = new Promise(function (resolve, reject) {
console.log(1);
setTimeout(function () {
console.log(2)
},0)
resolve(true);
}).then(function () {
console.log(3);
});
console.log(4);
// 1432
a变量是一个Promise,我们知道Promise是异步的,是指他的then()和catch()方法,Promise本身还是同步的,所以这里先执行a变量内部的Promise同步代码。(同步优先)
所以1先出现,遇到setTimeOut回调放到消息队列中,resolve(true),调用.then中函数放到消息队列中,最后的console.log是同步操作,所以此时出现14,同步这就执行完了。异步从消息队列里出来也就是.then,最后回调也从消息队列出来,所以顺序为1432
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("1111")
}, 1000)
resolve()
}).then(() => {
console.log('Hello')
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('2222')
}, 1000)
resolve()
}).then(() => {
console.log('hello vue')
})
})
new Promise((resolve, reject) =>{
setTimeout(() => {
console.log('3333')
}, 1)
resolve('Hello World!')
} ).then((data) => {
console.log(data)
})
console.log('我是同步')
上面这个例子:我们按照写的顺序把上面三个setTimeOut分别叫做S1,S2和S3, 三个,then叫做T1, T2, T3
遇到S1放到消息对列中,resolve函数调用T1放到消息队列中,遇到S3,放到消息队列中,resolve函数调用T3放到消息队列中
遇到同步console.log打印我是同步,同步执行完毕
回到消息队列,此时消息队列中有S1, S3, T1, T3, 按照之前提到的setTimeOut会在任务队列队尾执行,所以先执行T1, 打印Hello, 遇到S2放到任务队列中,遇到resolve调用T2放到消息队列中。执行T3 打印Hello World,执行T2,打印hello vue。现在可以执行setTimeOut了,S3延迟1ms执行,所以S3的打印结果3333,先出来然后是S1 1111, S2 2222。
打印顺序是:
我是同步
Hello
Hello World!
hello vue
3333
1111
2222