错误的理解引起的bug async await 执行顺序
今天有幸好碰到一个bug,让我知道了之前我对await async 的理解有点偏差。
错误的理解
之前我一直以为 await 后面的表达式,如果是直接返回一个具体的值就不会等待,而是继续执行async function 中的函数, 如下demo:
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),如果这样那么是一个错误的理解。
实际上MDN描述的暂停执行,并不是真正的暂停,而是让出了线程(跳出async函数体)然后继续执行后面的语句。
完整 demo code
vue created
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()
}
vue methods
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)
}
vuex 中
// 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
}
services api 中
注意在 testAsync 中 dispatch 了 initSchemeTimeTest,然后在调用了服务端的 schemeListTest
export async function schemeListTest (params) {
console.log('this is run server')
const data = await postTest(`/provid/spot/dailydeclareschemestatus/list`, params)
return data
}
common 中封装的 axiosServer
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 。
控制台运行结果:
执行顺序
js是单线程(同时只能干一件事情),
以上测试的关键点在于当程序碰到await 时,把后面的表达式执行一次,然后把resolve 函数或者reject 函数(await 操作符会把表达式的结果解析成promise 对象) push 回调队列,接着跳过当前这个async function ,执行async function 后面的代码,如上面代码中,执行 this.testNoAwait() 之后就跳过 this.test()这个方法,执行了
console.log('test last ...')
console.log('test end ...')
this.testSayHello()
至于什么时候知道这个promise 对象的状态,这就是事件循环的事情了,监听到这个异步的状态事件改变时,如果执行环境栈是空的那么就会执行取出回调队列中的回调,推入执行环境栈,然后继续async function 后面的语句。
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>
async awiat 执行顺序关键点
- 事件循环机制
- 回调队列
- 执行环境栈、入栈、出栈
- Promise 的构造函数是立即执行,但是他的成功、失败的回调函数是一个异步执行的回调
- Promise 的回调优先于 setTimeout 的任务队列
- async 返回promise 对象
- await 表达式的作用和返回值
总结
1、js 是单线程(同时只能做一件事情),在js引擎内部异步的处理是跟事件循环机制、以及回调队列有关
2、构造的promise 对象是立即执行传入的function
3、async function 是返回一个promise 对象
4、await 操作符会把表达式的结果进行解析成promise 对象
错误的理解引起的bug async await 执行顺序的更多相关文章
- async/await 执行顺序详解
随着async/await正式纳入ES7标准,越来越多的人开始研究据说是异步编程终级解决方案的 async/await.但是很多人对这个方法中内部怎么执行的还不是很了解,本文是我看了一遍技术博客理解 ...
- setTimeout,promise,promise.then, async,await执行顺序问题
今天下午看了好多关于这些执行顺序的问题 经过自己的实践 终于理解了 记录一下就拿网上出现频繁的代码来说: async function async1() { console.log('async1 ...
- Promise嵌套问题/async await执行顺序
/* 原则: 执行完当前promise, 会把紧挨着的then放入microtask队尾, 链后面的第二个then暂不处理分析, */ 一. new Promise((resolve, reject) ...
- 事件循环 EventLoop(Promise,setTimeOut,async/await执行顺序)
什么是事件循环?想要了解什么是事件循环就要从js的工作原理开始说起: JS主要的特点就是单线程,所谓单线程就是进程中只有一个线程在运行. 为什么JS是单线程的而不是多线程的呢? JS的主要用途就是与用 ...
- 理解ES7中的async/await
理解ES7中的async/await 优势是:就是解决多层异步回调的嵌套 从字面上理解 async/await, async是 "异步"的含义,await可以认为是 async w ...
- 创建HttpFilter与理解多个Filter代码的执行顺序
1.自定义的HttpFilter,实现Filter接口 HttpFilter package com.aff.filter; import java.io.IOException; import ja ...
- 理解Task和和async await
本文将详解C#类当中的Task,以及异步函数async await和Task的关系 一.Task的前世今生 1.Thread 一开始我们需要创建线程的时候一般是通过Thread创建线程,一般常用创建线 ...
- 理解C#中的 async await
前言 一个老掉牙的话题,园子里的相关优秀文章已经有很多了,我写这篇文章完全是想以自己的思维方式来谈一谈自己的理解.(PS:文中涉及到了大量反编译源码,需要静下心来细细品味) 从简单开始 为了更容易理解 ...
- async和await执行顺序
关于执行顺序和线程ID,写了一个小程序来检测学习: using System; using System.Net; using System.Threading; using System.Threa ...
随机推荐
- 开源通用爬虫框架YayCrawler-运行与调试
本节我将向大家介绍如何运行与调试YayCrawler.该框架是采用SpringBoot开发的,所以可以通过java –jar xxxx.jar的方式运行,也可以部署在tomcat等容器中运行. 首先 ...
- 从零开始学Kotlin-扩展函数(10)
从零开始学Kotlin基础篇系列文章 什么是扩展函数 扩展函数数是指在一个类上增加一种新的行为,我们甚至没有这个类代码的访问权限: Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 ...
- style和getComputedStyle(ff)和currentStyle
obj.style:这个方法只能JS只能获取写在html标签中的写在style属性中的值(style=”…”),而无法获取定义在<style type="text/css"& ...
- 『编程题全队』Alpha阶段发布说明
1. 这一版本的功能 (1)管理个人的任务事项,管理用户的提醒事项,提供一个简洁的操作界面,将其分类为全部.今天.明日.最近七天.更远.还有已完成,方便用户进行事务管理和整理. (2)提供一个便捷的备 ...
- tensorflow的一些基础用法
TensorFlow是一个采用数据流图,用于数值计算的开源软件库.自己接触tensorflow比较的早,可是并没有系统深入的学习过,现在TF在深度学习已经成了"标配",所以打算系统 ...
- Java线程池停止空闲线程是否有规则呢?
Java线程池中线程的数量超过核心线程的数量,且所有线程空闲,空闲时间超过keepAliveTime,会停止超过核心线程数量的线程,那么会保留哪些线程呢?是不是有规则呢? 测试代码: ThreadPo ...
- C#解析数组形式的json数据
在学习时遇到把解析json数据的问题,网上也搜了很多资料才得以实现,记录下来以便翻阅. 1. 下载开源的类库Newtonsoft.Json(下载地址http://json.codeplex.com/, ...
- 关于http请求时 安全协议问题 PKIX path building failed 解决办法
该问题的解决办法 1.在请求前需要将证书导入,不推荐 2.绕开安全协议处理 下面的代码时一段http请求并且绕开安全协议.可直接使用 /** * * @param url 需要请求的网 ...
- ssm框架junit简单测试_我写
第一步:导入相关jar包 主要是 junit包,和spring-test包 <dependecy> <groupId>junit</groupId> <art ...
- C#基础概念
1. 面向对象的思想主要包括:继承 多态 封装 ● 封装:用抽象的数据类型将数据和基于数据的操作封装在一起,数据被保护在抽象数据类型内部. ● 继承:子类拥有父类的所有 ...