JS程序执行与顺序实现详解
JS是一门单线程语言,指在一个时间只执行一个任务。虽然JS是单线程语言,但是它可以利用事件循环和回调实现异步编程。
1. JS代码的执行顺序
JS代码是从上往下一行一行执行的,但是在执行JS代码时,遇到如下情况就会把当前任务挂起,去执行其他任务:
- 等待Web API返回结果,例如:发起Ajax请求等。
- 等待定时器。
- 等待事件发生。
1.1 Web API的异步处理
当执行代码中遇到Web API时,JS并不会一直等待Web API的完成,而是先将代码执行完毕,然后将Web API相关的回调函数加入异步队列中排队等待Web API返回结果,待结果返回后,JS引擎从异步队列中读取相应回调函数。
示例:
console.log('start');
setTimeout(function() {
console.log('callback');
}, 1000);
console.log('end');
该示例中,先输出“start”,实际上是同步任务。接着执行定时器,将回调函数异步添加到任务队列中,然后输出“end”。但是需要等待1秒后,从异步队列中读取回调函数,输出“callback”。
1.2 Promise的异步处理
Promise是JS提供的异步编程的解决方案,ES6中的新特性。Promise可以将异步代码封装成Promise对象,实现更为优雅的异步代码的书写。
示例:
console.log('start');
new Promise((resolve, reject) => {
console.log('promise start');
setTimeout(() => {
resolve('promise callback');
}, 1000);
}).then((res) => {
console.log('promise then:', res);
});
console.log('end');
该示例中,Promise的执行流程:
- 首先输出“start”,接着执行Promise构造函数中的代码,输出“promise start”。
- 调用setTimeout函数,将回调函数异步加入任务队列,会异步执行回调函数。
- 输出“end”。
- 等待1秒后,从异步队列中读取回调函数,resolve Promise,将Promise对象标记为fulfill状态,同时执行then方法中的回调函数,输出“promise then:promise callback”。
1.3 await/async
async/await是ES8中的特性,比Promise更为优雅,在异步编程中经常被使用。async/await的实现原理就是Promise。
示例:
console.log('start');
function promise() {
return new Promise((resolve, reject) => {
console.log('promise start');
setTimeout(() => {
resolve('promise callback');
}, 1000);
});
}
async function asyncFunction() {
let res = await promise();
console.log('async function:', res);
}
asyncFunction();
console.log('end');
该示例中,async/await的执行流程:
- 首先输出“start”,接着执行promise函数中的代码,输出“promise start”。
- 调用setTimeout函数,将回调函数异步加入任务队列,会异步执行回调函数。
- 输出“end”。
- 等待1秒后,从异步队列中读取回调函数,resolve Promise,将Promise对象标记为fulfill状态。
- await promise()执行完毕后,执行async函数中的后续代码,输出“async function:promise callback”。
2. JS事件循环机制
JS的事件循环机制是负责读取异步队列中的回调函数,并在合适的时间将其加入调用栈中,执行回调函数。
2.1 微任务
微任务包含Promise回调函数、Node.js中的process.nextTick函数以及MutationObserver回调函数等,微任务中的回调函数执行速度比较快。
示例:
Promise.resolve().then(() => {
console.log('promise callback 1');
}).then(() => {
console.log('promise callback 2');
});
process.nextTick(() => {
console.log('process.nextTick callback');
});
console.log('start');
该示例中,输出顺序为:
- start
- process.nextTick callback
- promise callback 1
- promise callback 2
2.2 宏任务
宏任务包含定时器回调函数、事件回调函数等,宏任务执行速度较慢。
示例:
console.log('start');
setTimeout(() => {
console.log('setTimout callback');
}, 0);
Promise.resolve().then(() => {
console.log('promise callback 1');
}).then(() => {
console.log('promise callback 2');
});
console.log('end');
该示例中,输出顺序为:
- start
- end
- promise callback 1
- promise callback 2
- setTimout callback
3. JS代码执行顺序的总结
JS代码执行时:
- 如果遇到异步操作,则将回调函数加入异步队列中。
- 当同步代码执行完毕后,调用栈为空,JS引擎会读取异步队列中的回调函数,按照事件循环机制的规则取出执行。
- 执行完毕后,再次读取异步队列中的回调函数,如此反复,直到异步队列为空。
注意:代码的执行顺序不是绝对的,各种环境的实现可能会有所不同。
4. 小结
JS的异步执行需要遵守事件循环机制,异步操作将会加入异步队列,在适当的时间由任务队列中的事件循环机制调用栈中执行。虽然JS是单线程执行,但异步编程使代码的执行更为优雅和高效。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:js 程序执行与顺序实现详解 - Python技术站