参考:红宝书 es6入门
async
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里,就是generator的语法糖。
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
// ...
});
}
// 自动执行器
function spawn(genF) {
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
特性
- async关键字可以让函数具有异步特征,但总体上还是同步求值。
异步函数如果不包含await关键字,就和普通函数没什么区别
async function foo(){
console.log(1)
}
foo()
console.log(2)
// 1
// 2
async function bar(){
console.log(1)
await console.log(2)
console.log(4)
}
bar();
console.log(3)
// 1
// 2
// 3
// 4
- 返回值期待一个实现thenable接口的对象,这个对象可以提供给then()中的处理程序解包,如果不是就返回已经解决的Promise
async function baz(){
return {
then(callback){
callback('baz')
}
}
}
baz().then(console.log)
// baz
3.async内部报错机制:
①在没有显式返回Promise <rejected>情况下,依然是返回resolved态的promise,且里面异步错误无法捕获(Uncaught)。
async function p2(){
Promise.reject('1')
}
p2()
// Promise {<resolved>: undefined}
// Uncaught (in promise) 1
async function p3(){
return Promise.reject('2')
}
p3().catch(console.log)
// 2
②在异步函数内部抛出错误,会返回拒绝的promise
async function foo(){
console.log(1)
throw 3
}
console.log(2)
foo().catch(console.log)
// 1
// 2
// 3
③ 遇到await捕获的报错或者抛出错误的同步操作,会返回rejected的Promise,并且停止函数内后面代码的执行。
单独的Promise.reject()不会被异步函数捕获,而会抛出未捕获的错误。不过,对拒绝的Promise使用await则会释放(unwrap)错误值(将拒绝的Promise返回)
// await遇到返回错误的promise
async function foo(){
console.log(1)
await Promise.reject(3)
console.log(4) // 不执行
}
console.log(2)
foo().catch(console.log)
// 1
// 2
// 3
// await遇到同步操作情况
async function foo(){
console.log(1)
await (()=>{throw 3})()
}
console.log(2)
foo().catch(console.log)
// 1
// 2
// 3
await
作用:异步函数中的暂停和恢复的标志,await关键字会根据表达式是否是Promise来决定暂不暂停执行后面的代码(async函数中),让出js运行时的执行线程。与生成器函数中yield关键字作用是一样的。await关键字会尝试“解包”对象的值,然后把值传给表达式,在异步恢复异步函数的执行。
await - JavaScript | MDN
解包,类似于await 把这个promise主动then了一层,然后取出值
限制:
①必须在异步函数中使用
②异步函数特征不会扩展到嵌套函数,因此await关键字只能直接出现在异步函数的定义中
// 解包
async function foo(){
console.log(await Promise.resolve('foo'))
}
foo()
console.log(1)
// 1
// foo (异步打印)
async function baz(){
await new Promise(res =>{
settimeout(res,1000)
})
console.log('baz')
}
baz()
// baz (1000毫秒后)
async function re1(){
console.log(1);
console.log(await Promise.resolve(2)) // 让出执行权,交给异步消息队列
}
async function re2(){
console.log(3);
console.log(await 4) // 让出执行权,交给异步消息队列
}
re1();
re2();
console.log(5)
// 1
// 3
// 5
// 2
// 4
await关键字期待thenable接口的对象,这个对象由await去解包,如果不是,就会把这个值当作已经解决的promise,然后去解包
async function foo(){
const thenable = {
then(callback){
callback('foo')
}
}
console.log(await thenable)
}
foo()
// foo
async function bar(){
console.log(await 'bar')
}
bar()
// bar
异步错误捕获
js的同步错误捕获机制很强大,基本 try catch可以捕获所有同步错误,当然,是在try catch上下文中运行的同步错误。
参考:精读《捕获所有异步 error》
文章中讲了许多捕获异步异常的方法,详细可以点进去看,在这我只做总结
- async函数因为返回promise,基本用catch可以捕获到异步异常
- async函数体内用try catch 结构捕获await异常(常用)
- 如果支持await,可以用await一直链式解包
async function main() {
try {
const val1 = await firstStep();
const val2 = await secondStep(val1);
const val3 = await thirdStep(val1, val2);
console.log('Final: ', val3);
}
catch (err) {
console.error(err);
}
}
需要关注的是下面这种情况,即异步之中还有异步,或者dom事件监听
new Promise(() => {
setTimeout(() => {
throw new Error('err') // uncaught 无法捕获
}, 0)
}).catch((e) => {
console.log(e)
})
document.querySelector('button').addEventListener('click', () => {
throw new Error('err') // uncaught 无法捕获
})
解决办法如下:
①通过函数体内 try catch 来捕获
②设置全局错误监控
window.addEventListener('error')
window.addEventListener('unhandledrejection')
③Promise可以写成 Promise Chain
new Promise((res, rej) => {
setTimeout(res, 1000) // 1
})
.then((res, rej) => {
throw Error('err')
})
.catch((error) => {
console.log(error)
})
网友评论