function writeTransactionsToFile(transactions) { let writeStatus; try { fs.writeFileSync('transactions.txt', transactions); writeStatus = 'success'; } catch (error) { writeStatus = 'error'; } // 堆代码 duidaima.com // do something with writeStatus... }这是另一个我们想要一个取决于是否存在异常的值的实例。通常, 我们可能会在 try-catch 的范围之外创建一个可变变量,以便在其中和之后无错误地访问。但情况并非总是这样。只要有一个函数式的 try-catch 就不会这样。一个纯粹的 tryCatch() 函数避免了可变变量,并在我们的代码库中鼓励可维护性和可预测性。
function writeTransactionsToFile(transactions) { // 我们现在可以使用 const 了 const writeStatus = tryCatch({ tryFn: () => { fs.writeFileSync('transactions.txt', transactions); return 'success'; }, catchFn: (error) => 'error' }); // do something with writeStatus... }tryCatch() 函数
function tryCatch({ tryFn, catchFn }) { try { return tryFn(); } catch (error) { return catchFn(error); } }为了正确地讲述函数的作用,我们确保使用对象参数来明确参数名称——即使只有两个属性。因为编程不仅仅是达到目的的手段 - 我们还在讲述从开始到结束的代码库中的对象和数据的故事。TypeScript 在这样的情况下非常好用;我们看看一个泛型类型的 tryCatch() 可能是什么样子:
type TryCatchProps<T> = { tryFn: () => T; catchFn: (error: any) => T; }; function tryCatch<T>({ tryFn, catchFn }: TryCatchProps<T>): T { try { return tryFn(); } catch (error) { return catchFn(error); } }我们用 TypeScript 重写功能性 writeTransactionsToFile() :
function writeTransactionsToFile(transactions: string) { // 返回 'success' 或 'error' const writeStatus = tryCatch<'success' | 'error'>({ tryFn: () => { fs.writeFileSync('transaction.txt', transactions); return 'success'; }, catchFn: (error) => return 'error'; }); // do something with writeStatus... }我们使用 'success' | 'error' 联合类型来限制我们可以从 try 和 catch 回调中返回的字符串。
async function comment(comment: string) { type Status = 'error' | 'success'; let commentStatus: Status; try { const response = await fetch('https://api.mywebsite.com/comments', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ comment }), }); if (!response.ok) { commentStatus = 'error'; } else { commentStatus = 'success'; } } catch (error) { commentStatus = 'error'; } // do something with commentStatus... }我们再次需要在这里创建一个可变变量,以便它可以进入 try-catch 并且没有作用域错误地成功出来。我们像以前一样进行重构,这次,我们 async 了 try 和 catch 函数,从而 await 了 tryCatch() :
async function comment(comment: string) { type Status = 'error' | 'success'; // ⚠️ await because this returns Promise<Status> const commentStatus = await tryCatch<Status>({ tryFn: async () => { const response = await fetch('https://api.mywebsite.com/comments', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ comment }), }); // ⚠️ functional conditional return response.ok ? 'success' : 'error'; }, catchFn: async (error) => 'error', }); // do something with commentStatus... }可读性,模块化,和单一职责
2.每个函数只使用一个 try-catch
function processJSONFile(filePath) { let contents; let jsonContents; // ✅ 第一个 try-catch 块,用于处理文件读取错误 try { contents = fs.readFileSync(filePath, 'utf8'); } catch (error) { // 在这里记录错误 contents = null; } // ✅ 第二个 try-catch 块,用于处理 JSON 解析错误 try { jsonContents = JSON.parse(contents); } catch (error) { // 在这里记录错误 jsonContents = null; } return jsonContents; }但是 processJsonFile() 完全无视规则 2,同一个函数中的 try-catch 块都在。那么,我们通过将它们重构为各自的函数来解决这个问题:
function processJSONFile(filePath) { const contents = getFileContents(filePath); const jsonContents = parseJSON(contents); return jsonContents; } function getFileContents(filePath) { let contents; try { contents = fs.readFileSync(filePath, 'utf8'); } catch (error) { contents = null; } return contents; } function parseJSON(content) { let json; try { json = JSON.parse(content); } catch (error) { json = null; } return json; }但是我们现在有 tryCatch() - 我们可以做得更好:
function processJSONFile(filePath) { return parseJSON(getFileContents(filePath)); } const getFileContents = (filePath) => tryCatch({ tryFn: () => fs.readFileSync(filePath, 'utf8'), catchFn: () => null, }); const parseJSON = (content) => tryCatch({ tryFn: () => JSON.parse(content), catchFn: () => null, });我们正在做的只不过是消除异常——这就是这些新功能的主要工作。如果这种情况经常发生,为什么不创建一个“静音器”版本,在成功时返回 try 函数的结果,或者在错误时什么也不返回?
function tryCatch<T>(fn: () => T) { try { return fn(); } catch (error) { return null; } }将我们的代码进一步缩短为这样:
function processJSONFile(filePath) { return parseJSON(getFileContents(filePath)); } const getFileContents = (filePath) => tryCatch(() => fs.readFileSync(filePath, 'utf8')); const parseJSON = (content) => tryCatch(() => JSON.parse(content));附注:在命名标识符时,我建议我们尽可能地使用名词来表示变量,形容词来表示函数,而对于高阶函数……我们可以使用副词!就像一个故事,代码将更自然地阅读,并可能更好地理解。
const getFileContents = (filePath) => silently(() => fs.readFileSync(filePath, 'utf8')); const parseJSON = (content) => silently(() => JSON.parse(content));总结