javascript是单线程编程,意思就是javascript引擎一次只能执行一个语句。这样在出现长时间的请求的话,会阻塞主线程,为了不阻塞主线程,出现了javascript异步的概念。
javascript 同步异步理解:
设想这样的情景,你现在路过一个炸鸡店,闻着特别香,你实在抵挡不住炸鸡的诱惑,你就走上前,买了一份炸鸡。
同步的情况是:你点了一份炸鸡,然后你站到跟前等着啥也不干,炸鸡好了之后,你付钱给老板。
异步的情况是:你点了一份炸鸡,然后老板给你一张号码单,你把钱付了,玩着手机等,炸鸡好了之后,老板再叫你。
同步代码:
const makeChicken = () => {
console.log('炸鸡好了')
}
console.log('我要吃炸鸡')
makeChicken()
console.log('给你炸鸡钱')
console.log('玩手机')
执行结果:
我要吃炸鸡
炸鸡好了
给你炸鸡钱
玩手机
异步代码:
const makeChicken = () => {
setTimeout(() => {
console.log('炸鸡好了')
}, 2000)
}
console.log('我要吃炸鸡')
makeChicken()
console.log('给你炸鸡钱')
console.log('玩手机')
执行结果:
我要吃炸鸡
给你炸鸡钱
玩手机
炸鸡好了
异步实现逻辑:
事件循环、web APIs 和消息队列不是JavaScript引擎的一部分,它们是浏览器端的JavaScript运行环境或者Nodejs端的JavaScript运行环境的一部分。在Nodejs中,web APIs被C/C++ APIs替代。
当上面的代码加载在浏览器中,console.log('我要吃炸鸡'),被推到栈中然后当结束时从栈中被移除。然后,makeChicken()触发,然后它被推到了栈的顶部。
接下来setTimeOut( )函数被调用,所以它被推到了栈顶。setTimeOut( )有两个参数:一是回调,二是毫秒数。
这个setTimeOut( )方法在web APIs环境中开始了一个2秒的计时器。此时,setTimeOut( )执行完毕并被移出栈。之后,console.log('给你炸鸡钱'),被推到栈里,执行完后被移除。
同时,计时器到期了,现在这个回调被推到了消息队列。但是这回调不会被立即执行,这里就是事件循环插手的地方。
事件循环的作用是查看调用栈并确定调用栈是否为空。 如果调用栈为空,它会查看消息队列以查看是否有任何挂起的回调等待执行。
在上面的例子中,消息队列中有一个回调,并且此时调用栈已经空了。 因此,事件循环将回调推到栈顶部。
之后, console.log('炸鸡好了')被推到栈顶部,执行完后从堆栈中弹出。 此时,回调已执行完毕,因此被从栈中移除,程序最终完成。
异步的几种实现:
1)回调函数:
意思就是等到炸鸡好了再叫你。
function getYourFood() {
console.log('给你炸鸡')
}
function getChicken(callback) {
console.log('准备制作炸鸡')
setTimeout(() => {
console.log('炸鸡制作完成')
callback()
}, 2000)
}
getChicken(getYourFood)
执行结果:
我要炸鸡
准备制作炸鸡
炸鸡制作完成
给你炸鸡
2)promise
使我们可以主动的去执行后面的动作,而不是把回调函数给一个方法等在这个方法内部调用。
function makeChicken() {
return new Promise(((resolve) => {
console.log('准备制作炸鸡')
setTimeout(() => {
resolve('炸鸡成功')
console.log('炸鸡制作完成')
}, 2000)
console.log('炸鸡制作中')
}))
}
function getYourFood() {
return new Promise(((resolve) => {
resolve('炸鸡好了')
}))
}
console.log('我要炸鸡')
makeChicken().then(() => {
getYourFood().then((seccess) => {
console.log(seccess)
})
})
执行结果:
我要炸鸡
准备制作炸鸡
炸鸡制作中
炸鸡制作完成
炸鸡好了
3)async await
使异步代码看起来就是同步代码。我们添加一个关键字async让引擎知道哪个函数触发是异步的并且会返回一个promise,让我们使用await。
function makeChicken() {
return new Promise(((resolve) => {
console.log('准备制作炸鸡')
setTimeout(() => {
resolve('炸鸡成功')
console.log('炸鸡制作完成')
}, 2000)
console.log('炸鸡制作中')
}))
}
function getYourFood() {
return new Promise(((resolve) => {
resolve('炸鸡好了')
}))
}
async function OrderChicken() {
await makeChicken()
const result = await getYourFood()
console.log(result)
}
console.log('我要炸鸡')
OrderChicken()
运行结果:
我要炸鸡
准备制作炸鸡
炸鸡制作中
炸鸡制作完成
炸鸡好了
我觉得下面链接的有关异步的讲解特别好。
参考链接:
[译]理解异步JavaScript-事件循环https://mbd.baidu.com/newspage/data/landingshare?pageType=1&isBdboxFrom=1&context=%7B%22nid%22%3A%22news_9071904341435333508%22%2C%22sourceFrom%22%3A%22bjh%22%7D
[翻译]JavaScript异步进化史:Callbacks,Promises,Async/Await 上
https://mbd.baidu.com/newspage/data/landingshare?context=%7B%22nid%22%3A%22news_8754335365643315921%22%2C%22sourceFrom%22%3A%22bjh%22%7D&type=news
[翻译]JavaScript异步进化史之下篇
https://mbd.baidu.com/newspage/data/landingshare?context=%7B%22nid%22%3A%22news_9356479249406614478%22%2C%22sourceFrom%22%3A%22bjh%22%7D&type=news