在开发中,异步事件是项目必然需要处理的一个环节,也因为前端框架的崛起,通过框架实现的 SPA 已是快速开发的基本了,异步获取数据也就成了不可或缺的一环。
这两个名次从字面上很容易让人反过来理解
同步:指一件一件做事;
异步:很多事情同时处理;
类似过桥.同步是独木桥只能一个一个的过,异步是有多座桥,可同时过。
那么在 js 中处理异步事件的方法是什么呢?
最熟悉的就是回调函数了。例如网页与用户进行互动时,就需要接收一个回调函数;如setTimeout
,也都能通过传递回调函数在用户要求的时机去触发。先看例子:
function timeoutCallback(() {
console.log('start')
setTimeout(() => {
console.log('callback')
}, 1000)
console.log('done')
}
timeoutCallback()
在 setTimeout
被执行后,当过了指定的时间间隔之后,回调函数会被放到队列的末端,再等待事件处理它。
回调函数虽然在开发中十分常见,但也有许多难以避免的问题。例如最著名的“回调地狱”。
在 ES6 之后出现了 Promise,拯救了身陷在地狱的我们。
function matexiaPromise() {
return new Promise(resolve => {
console.log('promise')
resolve()
})
}
matexiaPromise()
.then(() => console.log('then 1'))
.then(() => setTimeout(() => console.log('setTimeout'), 0))
.then(() => console.log('then 2'))
.catch((err) => console.log('catch:', err))
Promise 是通过微任务队列来驱动它的;微任务队列的触发时机是在栈被清空时,JavaScript 引擎会先确认微任务队列有没有东西,有的话就优先执行,直到清空后才从队列拿出新任务到栈上。(在HTML 5 的Web API 标准 中,Event Loop 新增了微任务队列)
上述例子setTimeout就能很好的理解微任务与一般任务的差别,同时通过.catch语法来处理异步错误
从 Promise 问世之后,异步代码从回调地狱逐渐变成了优雅的函数式管道处理,但对于不熟悉的开发者来说,只不过是从回调地狱变成了 Promise 地狱。
在 ES8 中规范了新的 async await,虽然只是 Promise 和 Generator Function组合在一起的语法糖,但通过 async await便可以将异步事件用同步语法来处理。
function matexiawait(time) {
return new Promise(resolve => {
setTimeout(() => {
console.log('wait:', time)
resolve(time)
}, time)
})
}
await matexiawait(1000, () => console.log('bar'))
console.log('foo')
通过把 setTimeout包装成 Promise,再用 await关键字调用,可以看到结果会是同步执行的先出现 bar,再出现 foo,也就是开头提到的将异步事件写成同步处理。
在使用async/await时,由于await关键字只能在 async function 中执行,使用时务必要记得要同时使用。
另外在用循环处理异步事件时,需要注意在 ES6 之后提供的很多 Array 方法都不支持async/await语法
关注前端公众号(前端中心),获取更多前端技术,共同成长.