JavaScript 运行机制和运行顺序
JavaScript语言的一大特点就是单线程,也就是说,同一个时间所有事件只能从上到下,一件一件的处理。
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
下图就是主线程和任务队列的示意图
image.png
只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
异步任务分为(宏任务和微任务)
1、不管是同步还是异步,js都会按顺序执行,只是不等待异步的执行结果而已(并不是遇到异步的就绕过不执行,别蒙了)
2、同步的任务没有优先级之分,异步执行有优先级,先执行微任务(microtask队列),再执行宏任务(macrotask队列),同级别按顺序执行
3、await表示让出线程,继续执行后面的函数(执行当前函数后面的函数,不是当前函数里面的)
微任务: process.nextTick
,promise
,MutationObserver
宏任务:script
, setTimeout
,setInterval
,setImmediate
,I/O
,UI rendering
练习题: 如下
第一题:
function taskOne() {
console.log('task one ...')
setTimeout(() => {
Promise.resolve().then(() => {
console.log('task one micro in macro ...')
})
setTimeout(() => {
console.log('task one macro ...')
}, 0)
}, 0)
taskTwo()
}
function taskTwo() {
console.log('task two ...')
Promise.resolve().then(() => {
setTimeout(() => {
console.log('task two macro in micro...')
}, 0)
})
setTimeout(() => {
console.log('task two macro ...')
}, 0)
}
setTimeout(() => {
console.log('running macro ...')
}, 0)
taskOne()
Promise.resolve().then(() => {
console.log('running micro ...')
})
第二题:
async function t1 () {
console.log(1)
console.log(2)
await new Promise(resolve => {
setTimeout(() => {
console.log('t1p')
resolve()
}, 1000)
})
await console.log(3)
console.log(4)
}
async function t2() {
console.log(5)
console.log(6)
await Promise.resolve().then(() => console.log('t2p'))
console.log(7)
console.log(8)
}
t1()
t2()
console.log('end')
第三题:
async function async1() {
console.log( 'async1 start' )
await async2()
console.log( 'async1 end' )
}
async function async2() {
console.log( 'async2' )
}
console.log( 'script start' )
setTimeout( function () {
console.log( 'setTimeout' )
}, 0 )
async1();
new Promise( function ( resolve ) {
console.log( 'promise1' )
resolve();
} ).then( function () {
console.log( 'promise2' )
} )
console.log( 'script end' )
第四题:
async function testSometing() {
console.log("执行testSometing");
return "testSometing";
}
async function testAsync() {
console.log("执行testAsync");
return Promise.resolve("hello async");
}
async function test() {
console.log("test start...");
const v1 = await testSometing();
console.log(v1);
const v2 = await testAsync();
console.log(v2);
console.log(v1, v2);
}
test();
var promise = new Promise((resolve)=> { console.log("promise start.."); resolve("promise");});//3
promise.then((val)=> console.log(val));
console.log("test end...")
async await 执行顺序解析
async function testAsync() {
return "hello async";
}
let result = testAsync();
console.log(result)
// Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "hello async"}
从上面结果中可以看到async函数返回的是一个promise对象,如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。
// 如果asyn函数没有返回值
async function testAsync1() {
console.log("hello async");
}
let result1 = testAsync1();
console.log(result1);
//hello async
//Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: undefined}