JavaScript:像 Go 一样处理错误
类似 Promise 的日常功能
转换为async
/await
统一返回接口与async
/await
早在八月份,我就用巴西葡萄牙语写了一篇文章,解释如何使用async
/await
来隔离错误处理。
今天我将把它翻译成英文,但用不同的例子!
我很喜欢Go类似同步的方式处理副作用。我们来看看 Gonet/http
包中的这个例子:
func main() {
res, err := http.Get("http://example.com/")
if err != nil {
// handle `err`
}
// do something with `res`
}
或者也许是这个os
包:
func main() {
file, err := os.Open("words.txt")
if err != nil {
// handle `err`
}
// do something with `file`
}
除了实现细节之外,我想知道是否有办法用 JavaScript 编写这样的内容?
嗯,正如他们所说,有志者事竟成!😂
类似 Promise 的日常功能
如今,类似 Promise 的环境在我们之中很常见。
我们可以使用它来读取 Node.js 中的文件:
let util = require("util");
let fs = require("fs");
let read = util.promisify(fs.readFile);
function main() {
read("./test.js", { encoding: "utf8" })
.then(file => {
// do something with `file`
})
.catch(err => {
// handle `err`
});
}
main();
也许从 API 中获取一些数据:
let url = "https://dog.ceo/api/breeds/image/random";
function main() {
fetch(url)
.then(res => res.json())
.then(res => {
// do something with `res`
})
.catch(err => {
// handle `err`
});
}
main();
由于生性懒惰,我们创建函数来隐藏一些样板,这样我们就可以在整个代码库中编写更少的代码:
let readFile = require("./readFile");
function main() {
readFile("./test.js")
.then(file => {
// do something with `file`
})
.catch(err => {
// handle `err`
});
}
main();
// readFile.js
let util = require("util");
let fs = require("fs");
let read = util.promisify(fs.readFile);
module.exports = path => {
return read(path, { encoding: "utf8" })
.then(file => {
return file;
})
.catch(err => {
throw err;
});
};
和:
let api = require("./api");
function main() {
api.getRandomDog()
.then(res => {
// do something with `res`
})
.catch(err => {
// handle `err`
});
}
main();
// api.js
let url = "https://dog.ceo/api/breeds/image/random";
let api = {};
api.getRandomDog = () => {
return fetch(url)
.then(res => res.json())
.catch(err => {
throw err;
});
};
module.exports = api;
尽管如此,这里还是有很多重复,在这段代码片段的两边.then
都有。.catch
他们说async
/await
可以解决这个问题,那么...我们试试看吧?
转换为async
/await
async
让我们看看 Node.js 在/中的运行情况await
:
let readFile = require("./readFile");
async function main() {
try {
let res = await readFile("./test.js");
// do something with `file`
} catch (err) {
// handle `err`
}
}
main();
// readFile.js
let util = require("util");
let fs = require("fs");
let read = util.promisify(fs.readFile);
module.exports = async path => {
try {
let res = await read(path, { encoding: "utf8" });
return res;
} catch (err) {
throw err;
}
};
我们怎样才能用它来带走我们的狗呢?
let api = require("./api");
async function main() {
try {
let res = await api.getRandomDog();
// do something with `res`
} catch (err) {
// handle `err`
}
}
main();
// api.js
let url = "https://dog.ceo/api/breeds/image/random";
let api = {};
api.getRandomDog = async () => {
try {
let res = await fetch(url);
let json = await res.json();
return json;
} catch (err) {
throw err;
}
};
module.exports = api;
呼……我觉得我们把一个问题改成了另一个问题。现在try...catch
两个地方都有了。想想我们目前消费者/服务之间的接口,我们已经:
- 在我们的
main()
函数中,我们调用一个“服务”(readFile和api。) - 我们的“服务”功能返回一个
Promise
- 当完成后,我们的服务将返回有效载荷
- 当被拒绝时,我们的服务会抛出错误
嗯……也许这就是问题所在!对于已完成和已拒绝的情况,我们消费者/服务之间的接口是不同的。
刷新我们对顶部的Go示例的记忆:
func main() {
res, err := http.Get("http://example.com/")
if err != nil {
// handle `err`
}
// do something with `res`
}
看来我们对已完成和已拒绝的情况都有相同的界面!
让我们用最后一个async
/await
示例来尝试一下!
统一返回接口与async
/await
在我们的 Node.js 示例中:
let readFile = require("./readFile");
async function main() {
let [err, file] = await readFile("./test.js");
if (err) {
// handle `err`
}
// do something with `file`
}
main();
// readFile.js
let util = require("util");
let fs = require("fs");
let read = util.promisify(fs.readFile);
module.exports = async path => {
try {
let res = await read(path, { encoding: "utf8" });
return [null, res];
} catch (err) {
return [err, null]
}
};
我们的 Fetch API:
let api = require("./api");
async function main() {
let [err, res] = await api.getRandomDog();
if (err) {
// handle `err`
}
// do something with `res`
}
main();
// api.js
let url = "https://dog.ceo/api/breeds/image/random";
let api = {};
api.getRandomDog = async () => {
try {
let res = await fetch(url);
let json = await res.json();
return [null, json];
} catch (err) {
return [err, null]
}
};
module.exports = api;
做得好!!🎉🎉🎉
这正是我们想要的!我们的main()
函数看起来就像 Go 示例一样,现在我们已经将所有内容隔离到了try...catch
“服务”函数中。
使用这种方法,您可以清理您的 Node.js 中间件/控制器和前端,比如说使用 React/Redux,清理redux-thunks
或redux-saga
函数/生成器。
您还可以单独对这些“服务”功能进行单元测试,并确保它们返回预期的接口/数据。
鏂囩珷鏉ユ簮锛�https://dev.to/oieduardorabelo/javascript-handling-errors-like-go-3efk