异步编程(一):V8是如何实现微任务的?
什么是回调地狱?
如以下示例代码:
//result_callback:下载结果的回调函数
//url:需要获取URL的内容
function GetUrlContent(result_callback,url) {
let request = new XMLHttpRequest()
request.open('GET', url)
request.responseType = 'text'
request.onload = function () {
result_callback(request.response)
}
request.send()
}
function IDCallback(id) {
console.log(id)
let new_name_url = name_url + "?id="+id
GetUrlContent(NameCallback,new_name_url)
}
function NameCallback(name) {
console.log(name)
}
GetUrlContent(IDCallback,id_url)
异步回调模式影响到我们的编码方式,如果在代码中过多地使用异步回调函数,会将你的整个代码逻辑打乱,从而让代码变得难以理解,这也就是我们经常所说的回调地狱问题。
使用Promise解决回调地狱问题
改造后的代码如下:
fetch(id_url)
.then((response) => {
return response.text()
})
.then((response) => {
let new_name_url = name_url + "?id=" + response
return fetch(new_name_url)
}).then((response) => {
return response.text()
}).then((response) => {
console.log(response)//输出最终的结果
})
使用Generator函数实现更加线性化逻辑
像编写同步代码的方式来编写异步代码,比如:
function getResult(){
let id = getUserID()
let name = getUserName(id)
return name
}
由于getUserID()和getUserName()都是异步请求,如果要实现这种线性的编码方式,那么一个可行的方案就是执行到异步请求的时候,暂停当前函数,等异步请求返回了结果,再恢复该函数。
Async/await: 异步编程的“终极”方案
由于生成器函数可以暂停,因此我们可以在生成器内部编写完整的异步逻辑代码,不过生成器依然需要使用额外的co函数来驱动生成器函数的执行,这一点非常不友好。
基于这个原因,ES7 引入了async/await,这是JavaScript异步编程的一个重大改进,它改进了生成器的缺点,提供了在不阻塞主线程的情况下使用同步代码实现异步访问资源的能力。你可以参考下面这段使用async/await改造后的代码:
async function getResult() {
try {
let id_res = await fetch(id_url)
let id_text = await id_res.text()
console.log(id_text)
let new_name_url = name_url+"?id="+id_text
console.log(new_name_url)
let name_res = await fetch(new_name_url)
let name_text = await name_res.text()
console.log(name_text)
} catch (err) {
console.error(err)
}
}
getResult()
根据MDN定义:
async是一个通过异步执行并隐式返回Promise 作为结果的函数。
通常,await 可以等待两种类型的表达式:
- 可以是任何普通表达式;
- 也可以是一个Promise 对象的表达式。
如果await等待的是一个promise对象,他就会暂停执行生成器函数,直到Promise对象的状态变成resolve,才会继续执行,然后得到 resolve 的值,作为 await 表达式的运算结果。
function NeverResolvePromise(){
return new Promise((resolve, reject) => {})
}
async function getResult() {
let a = await NeverResolvePromise()
console.log(a)
}
getResult()
console.log(0)
这段代码,我们使用await 等待一个没有resolve的Promise,,getResult函数会一直等待下去。
如果await等待的对象已经变成了resolve状态,那么V8就会恢复该协程的执行,我们可以修改下上面的代码,来证明下这个过程:
function HaveResolvePromise(){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 0);
})
}
async function getResult() {
console.log(1)
let a = await HaveResolvePromise()
console.log(a)
console.log(2)
}
console.log(0)
getResult()
console.log(3)
如果await等待的是一个非Promise对象,比如await 100,那么V8会隐式地将await后面的100包装成一个已经resolve的对象,其效果等价于下面这段代码:
function ResolvePromise(){
return new Promise((resolve, reject) => {
resolve(100)
})
}
async function getResult() {
let a = await ResolvePromise()
console.log(a)
}
getResult()
console.log(3)
此文章为5月Day25学习笔记,内容来源于极客时间《图解 Google V8》,日拱一卒,每天进步一点点💪💪
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net