今天有幸好碰到一个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 执行顺序关键点

  1. 事件循环机制
  2. 回调队列
  3. 执行环境栈、入栈、出栈
  4. Promise 的构造函数是立即执行,但是他的成功、失败的回调函数是一个异步执行的回调
  5. Promise 的回调优先于 setTimeout 的任务队列
  6. async 返回promise 对象
  7. await 表达式的作用和返回值

总结

1、js 是单线程(同时只能做一件事情),在js引擎内部异步的处理是跟事件循环机制、以及回调队列有关

2、构造的promise 对象是立即执行传入的function

3、async function 是返回一个promise 对象

4、await 操作符会把表达式的结果进行解析成promise 对象

错误的理解引起的bug async await 执行顺序的更多相关文章

  1. async/await 执行顺序详解

    随着async/await正式纳入ES7标准,越来越多的人开始研究据说是异步编程终级解决方案的 async/await.但是很多人对这个方法中内部怎么执行的还不是很了解,本文是我看了一遍技术博客理解 ...

  2. setTimeout,promise,promise.then, async,await执行顺序问题

    今天下午看了好多关于这些执行顺序的问题  经过自己的实践 终于理解了  记录一下就拿网上出现频繁的代码来说: async function async1() { console.log('async1 ...

  3. Promise嵌套问题/async await执行顺序

    /* 原则: 执行完当前promise, 会把紧挨着的then放入microtask队尾, 链后面的第二个then暂不处理分析, */ 一. new Promise((resolve, reject) ...

  4. 事件循环 EventLoop(Promise,setTimeOut,async/await执行顺序)

    什么是事件循环?想要了解什么是事件循环就要从js的工作原理开始说起: JS主要的特点就是单线程,所谓单线程就是进程中只有一个线程在运行. 为什么JS是单线程的而不是多线程的呢? JS的主要用途就是与用 ...

  5. 理解ES7中的async/await

    理解ES7中的async/await 优势是:就是解决多层异步回调的嵌套 从字面上理解 async/await, async是 "异步"的含义,await可以认为是 async w ...

  6. 创建HttpFilter与理解多个Filter代码的执行顺序

    1.自定义的HttpFilter,实现Filter接口 HttpFilter package com.aff.filter; import java.io.IOException; import ja ...

  7. 理解Task和和async await

    本文将详解C#类当中的Task,以及异步函数async await和Task的关系 一.Task的前世今生 1.Thread 一开始我们需要创建线程的时候一般是通过Thread创建线程,一般常用创建线 ...

  8. 理解C#中的 async await

    前言 一个老掉牙的话题,园子里的相关优秀文章已经有很多了,我写这篇文章完全是想以自己的思维方式来谈一谈自己的理解.(PS:文中涉及到了大量反编译源码,需要静下心来细细品味) 从简单开始 为了更容易理解 ...

  9. async和await执行顺序

    关于执行顺序和线程ID,写了一个小程序来检测学习: using System; using System.Net; using System.Threading; using System.Threa ...

随机推荐

  1. dotTrace 每行执行时间和执行次数

    如果代码中出现效率问题,使用dotTrace来跟踪分析代码的效率问题还是很方便的.使用dotTrace不但可以看到每一个方法被调用的次数和总时间,而且可以引入源代码,查看源代码中每一行执行的次数和时间 ...

  2. 2013成都网赛1003 hdu 4730 We Love MOE Girls

    题意:有一个字符串,若以"desu"结尾,则将末尾的"desu"替换为"nanodesu",否则在字符串末尾加上"nanodesu ...

  3. Java之List排序功能举例

    package test_demo; import java.util.ArrayList; import java.util.Collections; import java.util.List; ...

  4. 普通的jdbc事务在插入数据后 下面的代码报错时 数据不会回滚 但是 spring的事务会回滚

    普通的jdbc事务在插入数据后 下面的代码报错时 数据不会回滚 但是 spring的事务会回滚

  5. 【题解】第n小的数

    题目描述 给你一些整数,你要找出第n小的数,也就是说把数据按升序排序后,输出下标是n的那个数,注意,我们规定下标是从0开始的.但是我们给出的数据的格式比较奇怪,我们是按区间给出,例如:给出两个区间:[ ...

  6. 02 使用Mybatis的逆向工程自动生成代码

    1.逆向工程的作用 Mybatis 官方提供了逆向工程,可以针对数据库表自动生成Mybatis执行所需要的代码(包括mapper.xml.Mapper.java.pojo). 2.逆向工程的使用方法 ...

  7. 初识Java Enum

    enum 的全称为 enumeration, 是 JDK 1.5  中引入的新特性,存放在 java.lang 包中. enum是关键字,感觉它跟class.interface是并列的,并且不能跟fi ...

  8. 【uoj34】 多项式乘法

    http://uoj.ac/problem/34 (题目链接) 题意 求两个多项式的乘积 Solution 挂个FFT板子. 细节 FFT因为要满足$n$是$2$的幂,所以注意数组大小. 代码 // ...

  9. apigateway-kong(三)Proxy规则

    本篇详细记录了Kong的代理功能及其路由功能和内部工作. Kong公开了几个可以通过两个配置属性进行调整的接口:proxy_listen,默认8000,它定义Kong将接受来自客户端的公共流量并将其代 ...

  10. XHR工厂的实现

    ajax这种常见的开发模式已经遍布我们日常的开发之中了,ajax本质还是采用一种轮询的模式,就是隔一段时间去发送一次http请求,获取数据,然后显示在页面之上,当然,ajax比起新兴的WebScoke ...