今天有幸亏碰到一个bug,让我知道了以前我对await async 的理解有点误差。html
以前我一直觉得 await 后面的表达式,若是是直接返回一个具体的值就不会等待,而是继续执行async function 中的函数, 以下demo:vue
method () { getParams () { let params = {} if (this.serachFrom.time !== 0) { params.month = this.serachFrom.time.substr(5, 2) params.year = this.serachFrom.time.substring(0, 4) } return params }, async testNoAwait () { console.log('run testNoAwait') return 'this is no await' }, async testAsync () { console.log('run testAsync') let params = this.getParams() const data = await this.$store.dispatch('initSchemeTimeTest', params) return data }, async test () { console.log('test start') const v1 = await this.testNoAwait() console.log(v1) const v2 = await this.testAsync() console.log(v2) console.log(v1, v2) } }, created () { console.log('this is run created ') this.test() console.log('test last ...') console.log('test end ...') }
如上程序我以前认为 await this.testNoAwait() 会直接执行完不会等待,继续执行 console.log(v1),若是这样那么是一个错误的理解。ios
实际上MDN描述的暂停执行,并非真正的暂停,而是让出了线程(跳出async函数体)而后继续执行后面的语句。web
async created () { console.log('this is run created ') this.test() // let data = this.test() // console.log(data) console.log('test last ...') console.log('test end ...') this.testSayHello() }
testSayHello () { console.log('this is run hello') }, getParams () { let params = {} if (this.serachFrom.time !== 0) { params.month = this.serachFrom.time.substr(5, 2) params.year = this.serachFrom.time.substring(0, 4) } return params }, testNoAwait () { console.log('run testNoAwait') return 'this is no await' }, async testAsync () { console.log('run testAsync') let params = this.getParams() const data = await this.$store.dispatch('initSchemeTimeTest', params) return data }, async test () { console.log('test start') const v1 = await this.testNoAwait() console.log(v1) const v2 = await this.testAsync() console.log(v2) console.log(v1, v2) }
// actions
async initSchemeTimeTest ({commit, state, dispatch}, params) { console.log('run initSchemeTimeTest') const data = await schemeListTest(params) console.log('开始返回结果') commit(types.SCHEME_DATA_TIME_LIST, data) return data }
注意在 testAsync 中 dispatch 了 initSchemeTimeTest,而后在调用了服务端的 schemeListTestvuex
export async function schemeListTest (params) { console.log('this is run server') const data = await postTest(`/provid/spot/dailydeclareschemestatus/list`, params) return data }
export function postTest (url, params) { return new Promise(async (resolve, reject) => { try { console.log('this is run common') const { data: { respHeader, respBody } } = await axiosServer({ url, type: 'post', params: { reqBody: params } }) if (respHeader.needLogin && process.env.NODE_ENV !== 'development') { Message.error(respHeader.message) location.href = condition.frontDomain + `/login?redirect=${encodeURI(condition.frontDomain + '/spot/race')}` reject(respHeader.message) } if (respHeader.resultCode === 0) { resolve(respBody || respHeader.message) } else { if (respHeader.resultCode === 21050 && respBody) { Message.error(respHeader.message) resolve(respBody) } else if (respHeader.message === '您没有该应用的权限') { location.href = 'frame.huidiancloud.com' } else { Message.error(respHeader.message) reject(respHeader.message) } } } catch (e) { reject(e) Message.error('系统繁忙,请稍后再试!') } }) }
若是按照以前的理解那么这个应该是输出了 run testNoAwait 以后继续输出 this is no await 。axios
js是单线程(同时只能干一件事情),api
以上测试的关键点在于当程序碰到await 时,把后面的表达式执行一次,而后把resolve 函数或者reject 函数(await 操做符会把表达式的结果解析成promise 对象) push 回调队列,接着跳过当前这个async function ,执行async function 后面的代码,如上面代码中,执行 this.testNoAwait() 以后就跳过 this.test()这个方法,执行了promise
console.log('test last ...') console.log('test end ...') this.testSayHello()
至于何时知道这个promise 对象的状态,这就是事件循环的事情了,监听到这个异步的状态事件改变时,若是执行环境栈是空的那么就会执行取出回调队列中的回调,推入执行环境栈,而后继续async function 后面的语句。app
vue 开始执行created 生命周期异步
输出:this is run created
输出:test start
执行:testNoAwait // 关键
输出 :run testNoAwait 以后 跳过 test() 函数 执行created 后面的语句
输出:test last ... 、test end ... 、this is run hello
程序回到
const v1 = await this.testNoAwait()
若是监听到这个异步事件完成 则开始执行 后面的代码因此会
输出:this is no await
下面这个 await 跟上面同理
const v2 = await this.testAsync()
await 后面的表达式执行一次,若是里面存在await 也是同理继续执行下去,执行完以后,跳过这个async function 等到异步操做完成了继续回到 const v2 这里执行。
这里须要注意的是在common 中的postTest 中构造的Promise 对象是当即执行传入的function 因此在 services api 输出了 this is run server 以后接着输出 this is run common
由于上面的列子不是很方便看,因此我写了一个简单的测试 :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="yes" name="apple-mobile-web-app-capable"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> <meta content="telephone=no,email=no" name="format-detection"> <meta name="App-Config" content="fullscreen=yes,useHistoryState=yes,transition=yes"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>async await demo</title> </head> <body> <h1>async await demo</h1> </body> <script> async function async1() { console.log('async1 start') await async2() console.log('async1 end') } async function async2() { console.log('async2') await async3() } async function async3() { console.log('async3') await async4() console.log('async4 end') } async function async4() { return new Promise(function (resolve, reject) { console.log('async4') resolve() }) } console.log('script start') setTimeout(function () { console.log('setTimeout') }, 0) async1(); new Promise(function (resolve) { console.log('promise1') resolve(); }).then(function () { console.log('promise2') }) console.log('script end') // script start async1 start async2 async3 async4 promise1 script end promise2 async4 end async1 end setTimeout </script> </html>
一、js 是单线程(同时只能作一件事情),在js引擎内部异步的处理是跟事件循环机制、以及回调队列有关
二、构造的promise 对象是当即执行传入的function
三、async function 是返回一个promise 对象
四、await 操做符会把表达式的结果进行解析成promise 对象