async/await 是一种特殊的语法,能够更好的处理promise,可以让你编写基于Promise的代码像同步一样。
### async
async 关键字放在函数之前,使得该函数总是返回一个promise对象。如果代码中显式 return 了一个值,那么函数的返回值将会被自动包装成resolved状态下的promise对象。
例如:
~~~js
async function fn() {
return 1;
}
fn().then(res => {
console.log(res) // 1
})
~~~
##### async函数的使用方式
~~~js
// 函数声明式
async function fn() { }
// 函数表达式
const fn = async function () { }
// ArrowFunc
const fn = async () => {}
// 对象中定义
let obj = {
async fn() {}
}
~~~
当async函数被调用执行的时候,会返回一个Promise的实例。当函数返回值时,promise会执行。如果函数在执行中抛出错误,那么会进入promise的rejected流程。
比如:
~~~js
const foo = async () => {
throw new Error('err');
}
foo()
.then(res => {
console.log(res);
})
.catch(err=> {
console.log(err) // Error: err
})
~~~
### await
await关键字只能在async函数中使用。可以用来等待Promise状态变成resolved并有返回值。await后面通常跟的是一个promise对象,如果不是,会立即被包装成resoled状态的promise。
~~~js
async function foo() {
let result = await 'ok'
console.log(result); // ok
}
foo();
~~~
1. 当用变量接收await的返回值时,值为promise为resolved状态下的值。
~~~js
const foo = async () => {
let result = await Promise.resolve('ok');
console.log(result); // ok
}
foo();
~~~
2. 函数执行中,遇到await,会首先返回promise,状态为pending,并且下面的代码会停止执行,待awiat后面的Promise执行完毕后才继续执行下面的代码,直到函数体中的代码全部执行完毕后,函数返回的promise状态才变成resolved。
~~~js
function bar() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok');
}, 1000);
})
}
async function foo() {
let result = await bar();
console.log(result); // ok
console.log('last');
}
console.log(foo());
~~~
输出顺序:
![](//upload-images.jianshu.io/upload_images/5256541-6b676362c10c14a3.png?imageMogr2/auto-orient/strip|imageView2/2/w/389/format/webp)
Markdown
首先打印出pending状态的promise对象。
等到await后面的异步函数执行完后,才依次打印出ok、last,同时Promise的状态成为resolved。
![](//upload-images.jianshu.io/upload_images/5256541-8cb6f6e09093d7c8.png?imageMogr2/auto-orient/strip|imageView2/2/w/462/format/webp)
Markdown
由此可以看出,当在async函数中运行,当遇到await关键字时,会等待关键字后面的函数执行完毕才运行后面的代码。当所有的执行完后函数返回的Promise对象的状态才变成resolved。
### 异常处理
我们知道在promise中是通过catch来捕获异常的。但是在async中则使用try/catch来捕获异常。
1. 如果await后面的 promise 正常resolve,await promise便会返回结果。但是在reject的情况下,便会抛出异常,并且这种异常需要用try/catch来捕获,否则会导致进程崩溃。
~~~js
const baz = () => {
return Promise.reject('Oops');
}
const foo = async () => {
try {
await baz();
} catch(e) {
// e 为 baz()返回的promise对象中 reject出来的值
console.log(e); // Oops
}
}
foo();
~~~
2. 如果try中有多个await,其中一个await后面的promise为reject,那么在catch中会抛出第一个异常。
如下:
~~~js
// 异步操作,返回promise对象
const bar = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('Oops bar error');
}, 1000)
})
}
const baz = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('Oops baz error');
}, 1000)
})
}
const foo = async () => {
try {
await bar();
await baz();
} catch(e) {
// e 为 bar()返回的promise对象中 reject出来的值
console.log(e); // Oops bar error
}
}
foo();
~~~
由此可以如果有多个 await 后面的promise都为reject状态时,只能捕获第一个异常。
3. 以下这种方式都会捕获到。由于是同步的效果,所以第二次捕获的异常是第一次捕获1s后。
~~~js
const fn = async () => {
try {
await bar();
} catch (e) {
console.log(e) // Oops bar error
}
try {
await baz();
} catch (e) {
console.log(e) // Oops baz error
}
}
fn();
// 第二次和第一次的顺序相隔1s
~~~
4. 如果await后面的promise中抛出异常,那么等同于async函数返回的 Promise 对象被reject。如下:
~~~js
async function foo() {
await new Promise((resolve, reject) => {
throw new Error('Oops');
});
}
foo()
.then(res => console.log('ok', res))
.catch(err => console.log('faile', err)) // faile Error: Oops
~~~
5. try里边的promise中的异常不会在catch中捕获,因为异常发生在promise中,只能通过promise的catch()捕获。
~~~js
const foo = () => {
return new Promise((resolve, reject) => {
resolve('ok');
})
}
const bar = () => {
try {
foo()
.then(res => {
let data = JSON.parse(res);
}).catch(err => {
console.log(err); // SyntaxError: Unexpected token o in JSON at position 0
})
} catch(e) {
// 这里不会捕获到promise中的异常
console.log(e);
}
}
bar();
~~~
#### 并行和串行
1. 串行
如果有多个await,那么会按照顺序一个个的执行。
~~~js
const p1 = async () => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ name: 'Rose' })
}, 1000);
})
}
const p2 = async () => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ name: 'Rose' })
}, 1000);
})
}
const getInfo = async () => {
console.time('total time');
let p1Info = await p1();
let p2Info = await p2();
console.timeEnd('total time'); // total time: 2006.705810546875ms
}
getInfo()
~~~
因为每个await后面的异步函数都会延时1s,所以总耗时大于2s。
2. 并行
如果有多个await,那么先将await后面的异步函数的返回值保存变量中。
~~~js
const getInfo = async() => {
console.time('total time');
const [p1Info, p2Info] = await Promise.all([
p1(),
p2()
])
console.timeEnd('total time'); // total time: 1003.5810546875ms
}
getInfo()
~~~
Promise.all()返回值是一个新的promise对象,值为数组。
3. 循环中的串行
~~~js
const p = async (name) => {
return new Promise(resolve => {
let user = {name};
setTimeout(() => {
resolve(user)
}, 1000)
})
}
let arr = ['bob', 'mike'];
const getInfo = async() => {
console.time('total time');
for (let name of arr) {
const p3Info = await p(name);
// 每隔1s输出一次
console.log(p3Info.name);
}
console.timeEnd('total time'); // total time: 2007.77783203125ms
}
~~~
输出顺序:
bob
mike
total time: 2007.77783203125ms
4. 循环中串行改并行
~~~js
const getInfo = async() => {
console.time('total time');
// 将所有的promise对象保存在数组中
let promises = arr.map(name => {
return p3(name);
})
for(let promise of promises) {
const Info = await promise;
console.log(Info.name);
}
console.timeEnd('total time'); // total time: 1006.291015625ms
}
~~~
### 总结:
#### async函数之前的关键字有两个作用:
1. 使它总是返回一个promise。
2. 允许在其中使用await。
#### await promise之前的关键字使得JavaScript等待,直到这个promise的状态为resolved
1. 如果是reject,则产生异常,需通过try/catch捕获。
2. 否则,它返回结果,所以我们可以将它的值赋值给一个变量。
async/await使我们少写promise.then/catch,但是不要忘记它们是基于promise的。
此外,网上很多关于promise的文章都会提到ajax的回调地狱,以此来说明promise的诞生只是用来解决异步的,其实不然。promise 只是解决异步的一种方式。
如果使用async/await,不仅代码简介,甚至有同步的feel。
promise是一种语法、一种形式,目前很多东西都是基于promise实现的,比如:jquery中的ajax,fetch,以及vue react中的很多功能也都使用了promise。所以promise是最最基本的。
0人点赞
[ECMAScript Next](/nb/16084136)
作者:一萧烟雨任平生
链接:https://www.jianshu.com/p/fd3d571e38db
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 文档说明
- 大厂面试题
- HTML
- 001.如何遍历一个dom树
- 002.为什么操作DOM会很慢
- 003.浏览器渲染HTML的步骤
- 004.DOM和JavaScript的关系
- JS
- 001.数组扁平化并去重排序
- 002.高阶函数
- 003.sort() 对数组进行排序
- 004.call 、 apply 和bind的区别
- 006.0.1+0.2为什么等于0.30000000000000004
- 011.var、let、const 的区别及实现原理?
- 010.new操作符都做了什么
- 009.a.b.c.d 和 a['b']['c']['d'],哪个性能更高?
- 016.什么是防抖和节流?有什么区别?如何实现?
- 017.['1', '2', '3'].map(parseInt) what & why ?
- 018.为什么 for 循环嵌套顺序会影响性能?
- 019.介绍模块化发展历程
- 020.push输出问题
- 021.判断数组的三个方法
- 022.全局作用域中,用 const 和 let 声明的变量不在 window 上,那到底在哪里?如何去获取?
- 023.输出以下代码的执行结果并解释为什么
- 024.ES6 代码转成 ES5 代码的实现思路是什么
- 025.为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因。
- 026.数组里面有10万个数据,取第一个元素和第10万个元素的时间相差多少
- 027.变量类型
- 028.原型和原型链
- 029.作用域和闭包
- 030. 异步
- 031.ES6/7 新标准的考查
- 024.事件冒泡/事件代理
- 025.手写 XMLHttpRequest 不借助任何库
- 026.什么是深拷贝?
- 0027.克隆数组的方法
- 0028.ES6之展开运算符(...)
- 0029.arguments
- 0030. requestAnimationFrame
- 0031.递归爆栈问题与解决
- 021.简单改造下面的代码,使之分别打印 10 和 20
- 032.箭头函数与普通函数
- 033.去除掉html标签字符串里的所有属性
- 034.查找公共父节点
- 035.Promise
- 0036.JSON.stringify ()
- CSS
- 001. BFC
- 002.介绍下 BFC、IFC、GFC 和 FFC
- 003.分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景
- 004.怎么让一个 div 水平垂直居中
- 005.重排重绘
- 006.inline/block/inline-block的区别
- 007.选择器的权重和优先级
- 008.盒模型
- 009.清除浮动
- 010.flex
- 011.nth-child和nth-of-type的区别
- 0012.overflow
- 0013.CSS3中translate、transform和translation的区别和联系
- 0014.flex
- 0015.px、em、rem
- 0016.width:100%
- 网络
- 001.讲解下HTTPS的工作原理
- 002.介绍下 HTTPS 中间人攻击
- 003.谈谈你对TCP三次握手和四次挥手的理解
- 004.A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态
- 005.简单讲解一下http2的多路复用
- 006. 介绍下 http1.0、1.1、2.0 协议的区别?
- 007.永久性重定向(301)和临时性重定向(302)对 SEO 有什么影响
- 008.URL从输入到页面展示的过程
- 009.接口如何防刷
- 010.http状态码?
- 0111.跨域/如何解决?
- 012.cookie 和 localStorage 有何区别?
- 013.Fetch API
- 014.跨域Ajax请求时是否带Cookie的设置
- 0015.协商缓存和强缓存
- 性能优化
- 001.前后端分离的项目如何seo
- 002.性能优化的方法
- 003.防抖和节流
- React
- 001.React 中 setState 什么时候是同步的,什么时候是异步的?
- 002.Virtual DOM 真的比操作原生 DOM 快吗?谈谈你的想法。
- 003.Hooks 的特别之处
- 004.元素和组件有什么区别?
- 005.什么是 Pure Components?
- 006.HTML 和 React 事件处理有什么区别?
- 007.如何将参数传递给事件处理程序或回调函数?
- 008.如何创建 refs?
- 009.什么是 forward refs?
- 010.什么是 Virtual DOM?
- 011.什么是受控组件、非受控组件?
- 012.什么是 Fragments ?
- 013.为什么React元素有一个$$typeof属性?
- 014.如何在 React 中创建组件?
- 015.React 如何区分 Class 和 Function?
- 016.React 的状态是什么?
- 017.React 中的 props 是什么?
- 018.状态和属性有什么区别?
- 019.如何在 JSX 回调中绑定方法或事件处理程序?
- 020.什么是 "key" 属性,在元素数组中使用它们有什么好处?
- 021.为什么顺序调用对 React Hooks 很重要?
- 022.setState如何知道该做什么?
- 023.hook规则?
- 024.Hooks 与 Class 中调用 setState 有不同的表现差异么?
- 025.useEffect
- 026.fiber的作用
- 027.context的作用?
- 028.setState何时同步何时异步?
- 029.react性能优化
- 030.fiber
- 031.React SSR
- 异步
- 001.介绍下promise
- 002.Async/Await 如何通过同步的方式实现异步
- 003.setTimeout、Promise、Async/Await 的区别
- 004.JS 异步解决方案的发展历程以及优缺点
- 005.Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?
- 006.模拟实现一个 Promise.finally
- 012.简单手写实现promise
- 015.用Promise对象实现的 Ajax
- 007.简单实现async/await中的async函数
- 008.设计并实现 Promise.race()
- 009.Async/await
- 010.珠峰培训promise
- git
- 001.提交但没有push
- 002.gitignore没有作用?
- Node
- 001.用nodejs,将base64转化成png文件
- Koa
- 001.koa和express的区别
- 数据库
- redux
- 001.redux 为什么要把 reducer 设计成纯函数
- 002.在 React 中如何使用 Redux 的 connect() ?
- 003.mapStateToProps() 和 mapDispatchToProps() 之间有什么区别?
- 004.为什么 Redux 状态函数称为 reducers ?
- 005.如何在 Redux 中发起 AJAX 请求?
- 006.访问 Redux Store 的正确方法是什么?
- 007.React Redux 中展示组件和容器组件之间的区别是什么?
- 008.Redux 中常量的用途是什么?
- 009.什么是 redux-saga?
- 设计模式
- 公司题目
- 001.饿了么
- 001.div垂直水平居中(flex、绝对定位)
- 002.React子父组件之间如何传值
- 003.Emit事件怎么发,需要引入什么
- 004.介绍下React高阶组件,和普通组件有什么区别
- 005.一个对象数组,每个子对象包含一个id和name,React如何渲染出全部的name
- 006.在哪个生命周期里写
- 007.其中有几个name不存在,通过异步接口获取,如何做
- 008.渲染的时候key给什么值,可以使用index吗,用id好还是index好
- 009.webpack如何配sass,需要配哪些loader
- 010.配css需要哪些loader
- 011.如何配置把js、css、html单独打包成一个文件
- 012.监听input的哪个事件,在什么时候触发
- 013.两个元素块,一左一右,中间相距10像素
- 014.上下固定,中间滚动布局如何实现
- 016.取数组的最大值(ES5、ES6)
- 017.apply和call的区别
- 018.ES5和ES6有什么区别
- 019.some、every、find、filter、map、forEach有什么区别
- 020.上述数组随机取数,每次返回的值都不一样
- 021.如何找0-5的随机数,95-99呢
- 022.页面上有1万个button如何绑定事件
- 023.如何判断是button
- 024.页面上生成一万个button,并且绑定事件,如何做(JS原生操作DOM)
- 025.循环绑定时的index是多少,为什么,怎么解决
- 026.页面上有一个input,还有一个p标签,改变input后p标签就跟着变化,如何处理
- 浏览器相关
- 001.性能优化
- 002.web安全
- 003.获取浏览器大小
- 004.从输入 URL 到页面加载完成的过程中都发生了什么事情?
- 后端
- 001.分布式
- zuku
- 字节
- webpack
- webpack的打包原理是什么
- Webpack-- 常见面试题
- webscoket