什么是 async function呢?按照MDN的解释,这是一种通过Promise来是书写异步调用更清晰的方式。
async
关键字表示出一个function是不是async function,使得这个function总是会执行Promise的resolved
或者rejected
。就是说即使我们在async function里throw errors,外部也捕获不到,而只会执行rejected
部分的代码。
常规的throw error和 try catch流程
假设我们有一个upperCase
方法,接受的参数必须是字符串,否则报错
function upperCase(name) {
if (typeof name !== "string") {
throw TypeError("name must be a string");
}
return name.toUpperCase();
}
module.exports = upperCase;
那么我们的Jest单元测代码可能是这样的:
"use strict";
const assert = require("assert");
const upperCase = require("../function");
describe("upperCase function", () => {
test("it throws when name is not provided", () => {
assert.throws(() => upperCase());
});
test("it throws when name is not a string", () => {
assert.throws(() => upperCase(9));
});
});
在类里也是一样,假设我们有个Person
类,构造函数接受的name
参数必须是字符串,否则抛出异常
class Person {
constructor(name) {
if (typeof name !== "string") {
throw TypeError("name must be a string");
}
this.name = name;
}
// some method here
}
module.exports = Person;
那么我们的Jest测试代码还是类似:
"use strict";
const assert = require("assert");
const Person = require("../index");
describe("Person class", () => {
test("it throws when name is not provided", () => {
assert.throws(() => new Person());
});
test("it throws when name is not a string", () => {
assert.throws(() => new Person(9));
});
});
测试一个async function
我们给Person
类添加一个async getData(url)
方法,按照同样的方式throw error,这个流程就不工作了!
class Person {
constructor(name) {
if (typeof name !== "string") {
throw TypeError("name must be a string");
}
this.name = name;
}
async getData(url) {
if (typeof url !== "string") {
throw TypeError("url must be a string");
}
// const response = await fetch(url)
// do stuff
}
}
module.exports = Person;
我们如果这样写Jest测试方法捕获不到期望的异常。
"use strict";
const assert = require("assert");
const Person = require("../index");
describe("Person methods", () => {
test("it throws when url is not a string", () => {
const valentinogagliardi = new Person("valentinogagliardi");
assert.throws(() => valentinogagliardi.getData());
});
});
得到的结果会是这样:
FAIL test/index.test.js
Person methods › it throws when url is not a string
assert.throws(function)
Expected the function to throw an error.
But it didn't throw anything.
Message:
Missing expected exception.
这是因为这时,我们在getData()
里throw的error,会以rejected
的.catch()
方式被捕获,而不是通常的try catch()
方式。
我们把测试代码改成下面这样就可以了:
"use strict";
const assert = require("assert");
const Person = require("../index");
describe("Person methods", () => {
test("it rejects when url is not a string", async () => {
expect.assertions(1);
const valentinogagliardi = new Person("valentinogagliardi");
await expect(valentinogagliardi.getData()).rejects.toEqual(
TypeError("url must be a string")
);
});
});
业务代码中对应的的catch就是这样:
const Person = require("../index");
const valentinogagliardi = new Person("valentinogagliardi");
valentinogagliardi
.getData()
.then(res => res)
.catch(err => console.error(err));
而下面使用try catch()
的业务代码是不会catch这相应的异常的:
const Person = require("../index");
async function whatever() {
try {
const valentinogagliardi = new Person("valentinogagliardi");
await valentinogagliardi.getData();
// do stuff with the eventual result and return something
} catch (error) {
throw Error(error);
}
}
whatever();
简单的小结一下:
- 在async function里不会以常规方式抛出异常
- async function总是返回Promise,会是
resolved
或者rejected
- 要捕获异常必须使用Promise的
.catch()
方法 - 在Jest单元测试中
assert.throws
只能测试常规方式的异常- async function的异常,需要通过
expect
加rejects
的方式捕获