1. async 函数介绍
ES2017 新规中引入了 async 函数,async 函数其实就是 Generator 函数的语法糖,只是省去了 next 方法递归的过程。
async 函数其实只是对 Generator 函数的改进而已。
-
内置执行器
a. Generator 函数的执行必须依靠执行器,而 async 自带了执行器,所以 async 函数的执行和普通函数一样,直接调用即可。 -
更好的语义:
a. async 表示函数里有异步操作
b. await 翻译过来就是等待的意思,需要等待后面的 Promise 执行结束才会执行下一步 -
更广的实用性
a. co 模块规定,yield 后面只能是 Thunk 函数或 Promise 对象,而 async 函数的await 命令后面,可以是 Promise 对象和原始类型的值 ( 数值、字符串、布尔值,但会自动转换 Promise.resolved() ) -
返回的是 Promise 对象
a. async 函数的返回值是 Promise 对象,同样可以使用 then 方法来指定下一步操作。这比 Generator 函数返回的 Iterator ( 迭代器 ) 对象方便多了。
// async 的返回值是 Promise,所以同样可以使用 then() 方法来指定回调函数
async function p1() {
// 返回的是字符串,会自动转换 ( Promise.resolve() )
return '成功'
}
console.log(p1());
// Promise {<fulfilled>: '成功'}
// 同样可以调用 then 方法
p1().then(value => {
console.log(value);
})
// 举个栗子:多少毫秒后输出一个值
function timeout(ms) {
// 返回一个 Promise
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, ms)
})
}
async function asyncPrint(value, ms) {
await timeout(ms)
console.log(value)
}
asyncPrint('hello world!', 2000)
async 函数返回的 Promise 对象,必须等到内部所有的 await 命令后面的 Promise 对象执行完毕,才会发生状态改变,除非遇到 return 语句或者抛出错误。
async function fn() {
await new Promise((resolve) => {
setTimeout(() => {
console.log(1)
resolve()
}, 2000)
})
// 抛出错误
// throw new Error('Error')
// return 语句
// return 'return 语句'
await new Promise((resolve) => {
setTimeout(() => {
console.log(2)
resolve()
}, 2000)
})
}
fn().then((value) => {
console.log(value)
})
2. await 命令
await 翻译过来就是等待的意思,需要等待命令后面的 Promise 执行结束才会执行下一步。
正常情况下,await 命令后面是一个 Promise 对象,返回该对象的结果。如果不算 Promise 对象,直接返回对应的值。
async function fn() {
let a = await 'a'
console.log(a)
let b = await 1
console.log(b)
let c = await setTimeout(()=>{}, 0)
console.log(c);
let d = await { a: 1 }
console.log(d)
let e = await function fun() {}
console.log(e)
}
fn()
// a
// 1
// Timeout 对象
// { a:1 }
// [Function fun]
如果 await 命令后面的 Promise 对象变为 rejected 状态,则 reject 的参数会被 catch 方法的回调函数接收。并且后续的代码都将不会再执行,async 函数也会中断执行。
async function fn() {
await Promise.reject('出错了')
await Promise.resolve('hello world') // 不会执行
}
fn().then((v) => {
console.log(v)
}).catch((e) => {
console.log(e)
})
// 出错了
如果我们希望前面的异步操作失败,依然不影响后面的异步操作时。
- 第一种解决方法:可以将 await 放在 try...catch 结构中,这样不管这个异步是否成功,后续的 await 都会接着执行。
- 第二种解决方法:直接在 Promise 对象上跟随 catch 方法,处理自身可能出现的错误。
// 第一种解决方法
async function fn() {
try {
await Promise.reject('出错了')
} catch (e) {/* 错误在这里进行处理 */}
return await Promise.resolve('hello world')
}
fn()
.then((v) => {
console.log(v)
})
// hello world
/****************************/
// 第二种解决方法
async function fn() {
await Promise.reject('出错了').catch((e) => {/* 错误在这里进行处理 */})
return await Promise.resolve('hello world')
}
fn()
.then((v) => {
console.log(v)
})
// hello world
3.错误处理
如果 await 后面的异步操作出错,就相当于 async 函数返回的 Promise 对象被 reject。
async function fn() {
await new Promise((resolve) => {
throw new Error('出错了')
})
}
fn()
.then((v) => console.log(v))
.catch((e) => console.log(e))
// Error: 出错了
上文提到,可以将 await 放在 try...catch 语句中,解决问题。多个 await 可以统一放在 try...catch 代码块中。
async function fn() {
try {
await fn1()
await fn2()
await fn3()
} catch (e) {
console.log(e)
}
}
3.1 多次重复尝试案例
function ajax(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.send()
xhr.onload = function () {
if (this.status >= 200 || this.status < 300) {
resolve(JSON.parse(this.response))
} else {
reject('加载失败')
}
}
})
}
const NUM_RETRIES = 3
async function test() {
let i = 0
for (; i < NUM_RETRIES; ++i) {
try {
let aa = await ajax('https://api.apiopen.top/api/sentences')
console.log(aa)
// 如果 await 操作成功,就会使用 break 语句跳出循环,
// 如果失败,错误被 catch 语句捕获,然后进入下一轮循环
break
} catch (err) {}
}
console.log(i) // 0
}
test()
4. await 和并行
在下面的代码中, 三个await 命令是互不关联的, 可却只能等上一个 await 命令结束才能执行下一个, 比较耗时。( 全部执行完, 需要 3s )
function timeout(text, ms) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(text);
resolve()
}, ms)
})
}
async function fn() {
await timeout(1, 1000)
await timeout(2, 1000)
await timeout(3, 1000)
}
fn()
// 1
// 2
// 3
// 每个 await 命令都需要等上一个 await 命令结束才能开始
所以我们完全可以让他们并行执行, 只需要在 Promise.all() 方法前加上 await 就可以完成。( 全部执行完只需要 1s )
// 方法一:Promise.all() 方法
function timeout(text, ms) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(text);
resolve()
}, ms)
})
}
async function fn() {
await Promise.all([timeout(1, 1000), timeout(2, 1000), timeout(3, 1000)])
}
fn()
// 1
// 2
// 3
// 1 2 3 是一起输出的, 总耗时时间为 1000ms
/*******************************************************/
// 方法二
function timeout(text, ms) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(text)
resolve()
}, ms)
})
}
async function fn() {
let a = timeout(1, 1000)
let b = timeout(2, 1000)
let c = timeout(3, 1000)
await a
await b
await c
}
fn()
// 1
// 2
// 3
// 1 2 3 是一起输出的, 总耗时时间为 1000ms
标签:function,await,console,log,JavaScript,Promise,async
From: https://www.cnblogs.com/bkzj/p/16823381.html