异步编程
目录
一.EventLoop
1.JavaScript 是单线程的语言
2.同步任务和异步任务
3.EventLoop执行过程
二.Promise
三.async与await
四.宏任务和微任务
一.EventLoop
1.JavaScript 是单线程的语言
基本概念
JavaScript 是一门单线程执行的编程语言。也就是说,同一时间只能做一件事情。
执行流程图
单线程执行任务队列的问题: 如果前一个任务非常耗时,则后续的任务就不得不一直等待,从而导致程序假死的问题。
2.同步任务和异步任务
为了防止某个耗时任务导致程序假死的问题,JavaScript 把待执行的任务分为了两类:
-
同步任务(synchronous)
-
异步任务(asynchronous)
同步代码:从上->下逐行执行,下一行必须等待上一行的结果才往下执行
异步代码:使用回调函数来接收结果
注意:setTimeout本身是同步的,但是里面的回调函数是异步执行的
<script>
// 同步代码
// 概念: 从上到下逐行执行, 下一行必须等待上一行的结果才行
console.log(1);
if (true){
console.log(2);
}
// 绑定是同步的, 事件处理函数会异步执行
document.addEventListener('click', () => {
console.log(3);
})
console.log(4);
setTimeout(() => {
console.log(5);
}, 0)
console.log(6);
for (let i = 0; i < 100; i++) {
console.log("for循环");
}
console.log(7);
// 异步代码:
// setTimeout(回调函数), setInterval(回调函数), ajax网络请求(回调函数), 事件处理函数(回调函数)
// 框架: this.$nextTick() -> Promise (回调函数)
</script>
?
3.EventLoop执行过程
-
同步任务由 JavaScript 主线程次序执行 -
异步任务委托给宿主环境执行 -
已完成的异步任务对应的回调函数,会被加入到任务队列中等待执行 -
JavaScript 主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行 -
JavaScript 主线程不断重复上面的第 4 步
执行栈:用来执行代码(
Web APIS:异步代码(js代码运行在浏览器环境中,调用浏览器中的功能来实现异步中回调的执行)
任务队列:任务队列中存放的都是异步任务中的回调函数(把异步任务中要执行的函数体放在任务队列中等着被执行栈执行)
注意:
代码在执行之前先进行函数预解析、变量预解析,解析完成后才开始执行代码
所有的异步任务只有在执行栈空闲(即主线程 | 同步代码执行完成后)的时候才会执行
JavaScript 主线程从“任务队列”中读取异步任务的回调函数,放到执行栈中依次执行。这个过程是循环不断的,所以整个的这种运行机制又称为 EventLoop(事件循环)
总结:主执行栈从任务队列中拿任务执行,这样的循环过程叫 EventLoop--展现异步代码如何执行的(事件循环)
二.Promise
概念: 关联(管理-包含)异步任务的对象, 将来拿到异步任务成功/失败的结果
<script>
console.log(1)
// 复习Promise
// 概念: 关联(管理-包含)异步任务的对象, 将来拿到异步任务成功/失败的结果
// 使用:
// new Promise()被实例化了, 函数体里的代码立刻执行(同步)
let p = new Promise((resolve, reject) => {
console.log(2)
// 内置传参数:
// resolve被调用 -> 管理任务成功 -> then()里回调函数执行
// reject() => 管理任务失败 -> catch()里回调函数执行
resolve('成功')
console.log(3);
})
console.log(4);
// p.then().catch()绑定了监听成功/失败结果的函数-并不会马上执行("resolve异步的")
p.then(res => {
console.log(5);
console.log(res)
}).catch(err => {
console.log(6);
console.log(err)
})
console.log(7);
</script>
?
三.async与await
基本概念
async await号称异步的终极解决方案,async await之后再无回调
async/await 是 ES8(ECMAScript 2017)引入的新语法,用来简化 Promise 异步操作。在 async/await 出 现之前,开发者只能通过链式.then() 的方式处理 Promise 异步操作。
基本使用
async 和 await 是一对关键字
-
async用于修饰一个函数, 表示一个函数是异步的 如果async函数内没有await,,那么async没有意义的,,全是同步的内容 只有遇到了await开始往下, 才是异步的开始 -
await 要用在 async 函数中 -
await 后面一般会跟一个promise对象, await会阻塞async函数的执行,直到等到 promise成功的结果(resolve的结果)
<script>
// async函数+await 取代了.then()
// new Promise()会立刻执行函数体里面一切代码
// resolve() 异步的
// await (代码会在async函数内, 等待停止) -> 右结合(先执行await右边的代码)
// 如果等待, 也还会把async外的代码继续执行 (如果有同步代码接着跑)
console.log(1)
function createPromiseObj() {
console.log(2);
return new Promise((resolve, reject) => {
console.log(3)
resolve('成功')
console.log(4);
})
console.log(5);
}
console.log(6);
testOne()
console.log(7);
async function testOne() {
console.log(8);
const res = await createPromiseObj()
console.log(res);
console.log(9);
}
console.log(10);
</script>
?
注意:只有await往后的才是异步代码
1. async和await是一对关键字,必须成对出现 2. async用于修饰一个函数,代表该函数是一个异步函数 3. await用于等待一个值,通常是一个promise对象 4. await后面是一个普通值,那会直接返回该值本身,如果await后面是一个promise,会返回promise成功的结果
await遇到基础值的情况:即await后面不是Promise对象
若await操作符后的表达式的值不是一个Promise,则返回该值本身,若该值不是一个Promise,阿瓦提会把该值转换为已正常处理的Promise(不是Promise,会转成Promise,还是会被当成异步任务放到任务队列中等待),然后等到其处理结果
<script>
// await 后面表达式 : promise对象、 具体值
// await遇到具体值, 会把它转换成Promise成功的结果 (Promise.resolve(2))
// Promise.resolve(2) -> Promise对象里调用resolve(2)
console.log(1)
async function fn() {
console.log(4)
const res = await 2
// 等同于
// const res = await Promise.resolve(2)
console.log(res)
}
fn()
console.log(3)
</script>
运行结果如下:
四.宏任务和微任务
基本概念:
JavaScript 把任务队列分为宏任务队列和微任务队列
注意:微任务会排在宏任务之前执行
流程图
<script>
console.log(1);
setTimeout(() => { // 宏任务
console.log(2);
}, 0)
console.log(3);
function pro(){
return new Promise((resolve, reject) => {
console.log(4);
resolve('成功') // 微任务(宿主->JS引擎)
console.log(5);
})
}
async function fn(){
console.log(6);
const res = await pro()
console.log(res);
}
fn()
console.log(7);
// 微任务会排列在宏任务之前
</script>
?练习题1:
<script>
console.log(1)
// 只要看到了加了时间, 那么一定是等时间满足了, 才会加入到任务队列中
setTimeout(function () {
console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
resolve(1000)
})
p.then(data => {
console.log(data)
})
console.log(3)
</script>
?
?练习题2:
<script>
console.log(1)
setTimeout(function () {
console.log(2)
new Promise(function (resolve) {
console.log(3)
resolve()
}).then(function () {
console.log(4)
})
})
new Promise(function (resolve) {
console.log(5)
resolve()
}).then(function () {
console.log(6)
})
setTimeout(function () {
console.log(7)
new Promise(function (resolve) {
console.log(8)
resolve()
}).then(function () {
console.log(9)
})
})
console.log(10)
</script>
<script>
// 0号宏任务 <script>里面的所有同步代码
// 宏任务执行完毕, 清空微任务队列里所有微任务, 再执行下一个宏任务
</script>
?
练习题3:
<script>
console.log(1)
// 只要看到了加了时间, 那么一定是等时间满足了, 才会加入到任务队列
setTimeout(function () {
console.log(2)
}, 0)
// new Promise()会立刻执行函数体里面一切代码
const p = new Promise((resolve, reject) => {
console.log(3)
resolve(1000) // 标记为成功,指定.then(); reject()表示为失败,执行catch()
console.log(4)
})
p.then(data => {
console.log(data)
})
console.log(5)
</script>
?
?练习题4:
resolve()不会阻塞代码往下执行 await会阻塞代码往下执行
微任务同时有多个时,后来者居上(先执行)
<script>
// 微任务同时有多个, 往前面插入排队
new Promise((resolve, reject) => {
resolve(1) // 状态凝结 (Promise对象已经有了结果->后面的resolve和reject都没用)
resolve(100)
new Promise((resolve, reject) => {
resolve(2)
}).then(data => {
console.log(data)
})
}).then(data => {
console.log(data)
})
console.log(3)
</script>
?
?练习题5:
<script>
setTimeout(() => {
console.log(1)
}, 0)
new Promise((resolve, reject) => {
console.log(2)
resolve('p1')
new Promise((resolve, reject) => {
console.log(3)
setTimeout(() => {
resolve('setTimeout2')
console.log(4)
}, 0)
resolve('p2')
}).then(data => {
console.log(data)
})
setTimeout(() => {
resolve('setTimeout1')
console.log(5)
}, 0)
}).then(data => {
console.log(data)
})
console.log(6)
</script>
?
总结:
resolve会调用.then()方法,放在异步任务队列中的微任务队列中 resolve不会阻塞后面代码的执行
状态凝结:Promise对象已经有了结果,后面的resolve和reject都不执行
微任务同时有多个时,后来者居上(先执行)
定时器: 相同时间,按放在宏任务队列中的先后顺序执行 不同时间,时间短的在前先执行
|