今天有幸好碰到一个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. 团队伊始——DreamCatcher

    我们的团队,队名是DreamCatcher,中文意思是追梦人,它是一首歌曲,所属专辑是<新世纪的曙光>. 这是一首很好听的歌曲,里面有一句歌词是: I'm a dream catcher ...

  2. Alpha 冲刺一

    团队成员 051601135 岳冠宇 051604103 陈思孝 031602629 刘意晗 031602248 郑智文 031602234 王淇 会议照片 项目燃尽图 项目进展 界面(简陋) 登录界 ...

  3. psql -- PostgreSQL 交互终端

    psql --  PostgreSQL 交互终端 用法:psql [option...] [dbname [username]] 描述:psql 是一个以终端为基础的 PostgreSQL 前端.它允 ...

  4. Git 使用中显示“Another git process seems to be running in this repository...”问题解决

    一.引言:问题回忆 这几天,我同时在使用vs2017自带的git管理工具和git bash命令行工具对于同一个工作区进行了git操作管理. 其中,当我在vs2017中对文件进行了更改,突然脑洞大开,想 ...

  5. BOM之navigator对象和用户代理检测

    前面的话 navigator对象现在已经成为识别客户端浏览器的事实标准,navigator对象是所有支持javascript的浏览器所共有的.本文将详细介绍navigator对象和用户代理检测 属性 ...

  6. BZOJ5461 PKUWC2018Minimax(概率期望+线段树合并+动态规划)

    离散化后,容易想到设f[i][j]为i节点权值为j的概率,不妨设j权值在左子树,则有f[i][j]=f[lson][j](pi·f[rson][1~j]+(1-pi)·f[rson][j~m]). 考 ...

  7. KUR-Couriers

    题目链接: QwQ Solution: 以权值为下标,对每个点建树 对于x点,以它为根的树涵盖的是1到x区间内每个数出现的次数 Code: #include<bits/stdc++.h> ...

  8. 内置函数zip()

    zip有拉链的意思,zip函数像拉链一样将0个或多个可迭代对象按相同位置组合成一个zip对象,该zip对象的每个元素是由每个可迭代对象的相同位置的元素组成的元祖. 如果zip中有多个序列,而各序列的长 ...

  9. HTTP ERROR 400 Bad Request

    一springmvc项目中我新增记录完全ok,编辑就是不行,后台方法进不去.老是报错HTTP ERROR 400 Bad Request. 经过查询,说是400表示请求中的语法错误. 我把新增记录的请 ...

  10. 利用EF和C#泛型实现通用分页查询

    利用EF和C#泛型实现通用分页查询       Entity Framework 是微软以 ADO.NET 为基础所发展出来的对象关系对应 (ORM) 解决方案,是微软的ORM框架.此框架将数据库中的 ...