自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

TypeScript 出現(xiàn) Go 和 Rust的 錯誤? 沒有Try/Catch?

開發(fā) 后端
JavaScript 依靠拋出異常來處理錯誤,而 Go 和 Rust 將它們視為值。 你可能認(rèn)為這沒什么大不了的……但是,孩子,這可能聽起來微不足道; 然而,它改變了游戲規(guī)則。

那么,讓我們從我的一些背景故事開始。 我是一名擁有大約十年經(jīng)驗的軟件開發(fā)人員,最初使用 PHP,然后逐漸過渡到 JavaScript。

大約五年前,我開始使用 TypeScript,從那時起,我就再也沒有回到過 JavaScript。 當(dāng)我開始使用它的那一刻,我認(rèn)為它是有史以來最好的編程語言。 每個人都喜歡它; 每個人都用它……這只是最好的,對吧? 正確的? 正確的?

是的,然后我開始嘗試其他語言,更現(xiàn)代的語言。 首先是 Go,然后我慢慢地將 Rust 添加到我的列表中(感謝 Prime)。

當(dāng)您不知道不同事物的存在時,就很難錯過事物。

我在說什么? Go 和 Rust 的共同點是什么? 錯誤。 對我來說最突出的事情。 更具體地說,這些語言如何處理它們。

JavaScript 依靠拋出異常來處理錯誤,而 Go 和 Rust 將它們視為值。 你可能認(rèn)為這沒什么大不了的……但是,孩子,這可能聽起來微不足道; 然而,它改變了游戲規(guī)則。

讓我們來看看它們。 我們不會深入研究每種語言; 我們想知道一般方法。

讓我們從 JavaScript/TypeScript 和一個小游戲開始。

給自己五秒鐘的時間來查看下面的代碼并回答為什么我們需要將其包裝在 try/catch 中。

try {
  const request = { name: “test”, value: 2n };
  const body = JSON.stringify(request);
  const response = await fetch("https://example.com", {
    method: “POST”,
    body,
  });
  if (!response.ok) {
    return;
  }
  // handle response
} catch (e) {
  // handle error
  return;
}

所以,我假設(shè)你們大多數(shù)人都猜到即使我們正在檢查response.ok,fetch 方法仍然會拋出錯誤。 response.ok 僅“捕獲”4xx 和 5xx 網(wǎng)絡(luò)錯誤。 但當(dāng)網(wǎng)絡(luò)本身出現(xiàn)故障時,就會拋出錯誤。

但我想知道有多少人猜到 JSON.stringify 也會拋出錯誤。 原因是請求對象包含bigint(2n)變量,JSON不知道如何字符串化。

所以第一個問題是,就我個人而言,我認(rèn)為這是有史以來最大的 JavaScript 問題:我們不知道什么會引發(fā)錯誤。 從 JavaScript 錯誤的角度來看,它與以下內(nèi)容相同:

try {
  let data = “Hello”;
} catch (err) {
  console.error(err);
}

JavaScript 不知道; JavaScript 不在乎。 你應(yīng)該知道。

第二件事,這是完全可行的代碼:

const request = { name: “test”, value: 2n };
const body = JSON.stringify(request);
const response = await fetch("https://example.com", {
  method: “POST”,
  body,
});
if (!response.ok) {
  return;
}

沒有錯誤,即使這可能會破壞您的應(yīng)用程序。

現(xiàn)在,在我的腦海中,我可以聽到,“有什么問題,只要在任何地方使用 try/catch 就可以了。” 第三個問題來了:我們不知道拋出的是哪一個。 當(dāng)然,我們可以通過錯誤消息進行猜測,但是對于有很多可能發(fā)生錯誤的地方的更大的服務(wù)/功能呢? 您確定通過一次 try/catch 正確處理了所有這些問題嗎?

好吧,是時候停止對 JS 的挑剔,轉(zhuǎn)向其他的事情了。 讓我們從這段 Go 代碼開始:

f, err := os.Open(“filename.ext”)
if err != nil {
  log.Fatal(err)
}
// do something with the open *File f

我們正在嘗試打開一個返回文件或錯誤的文件。 您會經(jīng)??吹竭@種情況,主要是因為我們知道哪些函數(shù)總是返回錯誤。 你永遠(yuǎn)不會錯過任何一個。 這是將錯誤視為值的第一個示例。 您指定哪個函數(shù)可以返回它們,您返回它們,您分配它們,您檢查它們,您使用它們。

它也沒有那么豐富多彩,這也是 Go 受到批評的事情之一——“錯誤檢查代碼”,其中 if err != nil { .... 有時需要比其他代碼行更多的代碼。

if err != nil {
  …
  if err != nil {
    …
    if err != nil {
      … 
    }
  } 
}
if err != nil {
  … 
}
…
if err != nil {
  … 
}

仍然完全值得付出努力,相信我。

最后,鐵銹:

let greeting_file_result = File::open(“hello.txt”);
let greeting_file = match greeting_file_result {
  Ok(file) => file,
  Err(error) => panic!("Problem opening the file: {:?}", error),
};

這里顯示的三個中最冗長的一個,具有諷刺意味的是,也是最好的一個。 因此,首先,Rust 使用其令人驚嘆的枚舉來處理錯誤(它們與 TypeScript 枚舉不同?。?。 無需詳細(xì)介紹,這里重要的是它使用一個名為 Result 的枚舉,它有兩個變體:Ok 和 Err。 正如您可能猜到的,Ok 保存一個值,Err 保存……令人驚訝的是,一個錯誤:D。

它還有很多方法可以更方便地處理它們,以緩解 Go 問題。 最知名的是? 操作員。

let greeting_file_result = File::open(“hello.txt”)?;

這里的總結(jié)是,Go 和 Rust 總是知道哪里可能出現(xiàn)錯誤。 它們迫使你在它出現(xiàn)的地方(大部分)處理它。 沒有隱藏的,沒有猜測,沒有令人驚訝的面孔破壞應(yīng)用程序。

而且這種方法更好。 一英里。

好吧,是時候說實話了; 我撒了一點謊。 我們不能讓 TypeScript 錯誤像 Go / Rust 那樣工作。 這里的限制因素是語言本身; 它沒有合適的工具來做到這一點。

但我們能做的就是盡量讓它相似。 并使其變得簡單。

從這個開始:

export type Safe<T> =
  | {
    success: true;
    data: T;
  }
  | {
    success: false;
    error: string;
  };

這里沒什么特別的,只是一個簡單的泛型類型。 但這個小寶貝可以完全改變代碼。 您可能會注意到,這里最大的區(qū)別是我們要么返回數(shù)據(jù),要么返回錯誤。 聽起來很熟悉?

另外……第二個謊言,我們確實需要一些嘗試/捕獲。 好消息是我們只需要大約兩個,而不是 100,000 個。

export function safe<T>(promise: Promise<T>, err?: string): Promise<Safe<T>>;
export function safe<T>(func: () => T, err?: string): Safe<T>;
export function safe<T>(
  promiseOrFunc: Promise<T> | (() => T),
  err?: string,
): Promise<Safe<T>> | Safe<T> {
  if (promiseOrFunc instanceof Promise) {
    return safeAsync(promiseOrFunc, err);
  }
  return safeSync(promiseOrFunc, err);
}

async function safeAsync<T>(
  promise: Promise<T>, 
  err?: string
): Promise<Safe<T>> {
  try {
    const data = await promise;
    return { data, success: true };
  } catch (e) {
    console.error(e);
    if (err !== undefined) {
      return { success: false, error: err };
    }
    if (e instanceof Error) {
      return { success: false, error: e.message };
    }
    return { success: false, error: "Something went wrong" };
  }
}

function safeSync<T>(
  func: () => T, 
  err?: string
): Safe<T> {
  try {
    const data = func();
    return { data, success: true };
  } catch (e) {
    console.error(e);
    if (err !== undefined) {
      return { success: false, error: err };
    }
    if (e instanceof Error) {
      return { success: false, error: e.message };
    }
    return { success: false, error: "Something went wrong" };
  }
}

“哇哦,真是個天才。 他為 try/catch 創(chuàng)建了一個包裝器?!?是的你是對的; 這只是一個包裝器,以我們的 Safe 類型作為返回類型。 但有時您所需要的只是簡單的事情。 讓我們將它們與上面的示例結(jié)合起來。

舊的(16行):

try {
  const request = { name: “test”, value: 2n };
  const body = JSON.stringify(request);
  const response = await fetch("https://example.com", {
    method: “POST”,
    body,
  });
  if (!response.ok) {
    // handle network error
    return;
  }
  // handle response
} catch (e) {
  // handle error
  return;
}

新的(20行):

const request = { name: “test”, value: 2n };
const body = safe(
  () => JSON.stringify(request),
  “Failed to serialize request”,
);
if (!body.success) {
  // handle error (body.error)
  return;
}
const response = await safe(
  fetch("https://example.com", {
    method: “POST”,
    body: body.data,
  }),
);
if (!response.success) {
  // handle error (response.error)
  return;
}
if (!response.data.ok) {
  // handle network error
  return;
}
// handle response (body.data)

所以,是的,我們的新解決方案更長,但性能更好,原因如下:

  • 沒有try/catch。
  • 我們處理發(fā)生的每個錯誤。
  • 我們可以為特定函數(shù)指定錯誤消息。
  • 我們有一個很好的從上到下的邏輯,所有錯誤都在頂部,然后只有響應(yīng)在底部。

但現(xiàn)在王牌來了。 如果我們忘記檢查這一點會發(fā)生什么:

if (!body.success) {
  // handle error (body.error)
  return;
}

問題是……我們不能。 是的,我們必須進行這項檢查。 如果不這樣做,body.data 將不存在。 LSP 將通過拋出“‘Safe<string>’類型上不存在屬性‘data’”錯誤來提醒我們。 這一切都?xì)w功于我們創(chuàng)建的簡單 Safe 類型。 它也適用于錯誤消息。 在檢查 !body.success 之前,我們無法訪問 body.error。

現(xiàn)在我們應(yīng)該欣賞 TypeScript 以及它如何改變 JavaScript 世界。

以下內(nèi)容也是如此:

if (!response.success) {
  // handle error (response.error)
  return;
}

我們不能刪除 !response.success 檢查,因為否則,response.data 將不存在。

當(dāng)然,我們的解決方案并非沒有問題。 最重要的一點是,您必須記住使用我們的安全包裝器來包裝可能引發(fā)錯誤的 Promise/函數(shù)。 這種“我們需要知道”是我們無法克服的語言限制。

聽起來可能很難,但事實并非如此。 您很快就會開始意識到,代碼中幾乎所有的 Promise 都可能會拋出錯誤,而同步函數(shù)也會拋出錯誤,您知道它們,但它們并不多。

不過,您可能會問,值得嗎? 我們認(rèn)為是的,而且它在我們的團隊中運行得很好:)。 當(dāng)您查看更大的服務(wù)文件時,任何地方都沒有 try/catch,每個錯誤都在出現(xiàn)的地方進行處理,具有良好的邏輯流程……它看起來不錯。

以下是使用 SvelteKit FormAction 的真實示例:

export const actions = {
  createEmail: async ({ locals, request }) => {
    const end = perf(“CreateEmail”);
    const form = await safe(request.formData());
    if (!form.success) {
      return fail(400, { error: form.error });
    }
    const schema = z
      .object({
        emailTo: z.string().email(),
        emailName: z.string().min(1),
        emailSubject: z.string().min(1),
        emailHtml: z.string().min(1),
      })
    .safeParse({
      emailTo: form.data.get("emailTo"),
      emailName: form.data.get("emailName"),
      emailSubject: form.data.get("emailSubject"),
      emailHtml: form.data.get("emailHtml"),
    });
    if (!schema.success) {
      console.error(schema.error.flatten());
      return fail(400, { form: schema.error.flatten().fieldErrors });
    }
    const metadata = createMetadata(URI_GRPC, locals.user.key)
    if (!metadata.success) {
      return fail(400, { error: metadata.error });
    }
    const response = await new Promise<Safe<Email__Output>>((res) => {
      usersClient.createEmail(schema.data, metadata.data, grpcSafe(res));
    });
    if (!response.success) {
      return fail(400, { error: response.error });
    }
    end();
    return {
      email: response.data,
    };
  },
} satisfies Actions;

這里有幾點需要指出:

  • 我們的自定義函數(shù) grpcSafe 幫助我們處理 gGRPC 回調(diào)。
  • createMetadata 在內(nèi)部返回 Safe,所以我們不需要包裝它。
  • zod 庫使用相同的模式:) 如果我們不進行 schema.success 檢查,我們就無法訪問 schema.data。

是不是看起來很干凈呢? 所以嘗試一下吧! 也許它也非常適合您:)

謝謝閱讀。

附: 看起來很相似?

f, err := os.Open(“filename.ext”)
if err != nil {
  log.Fatal(err)
}
// do something with the open *File f
const response = await safe(fetch(“https://example.com"));
if (!response.success) {
  console.error(response.error);
  return;
}
// do something with the response.data
責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2020-09-27 07:48:40

不用try catch

2025-04-29 08:05:00

JavaScript錯誤處理開發(fā)

2020-08-24 13:35:59

trycatchJava

2021-01-05 07:54:55

事項trycatch

2024-05-24 08:59:15

2017-11-02 15:26:10

JavaScriptasync錯誤

2009-12-02 19:56:33

PHP中try{}ca

2009-02-17 09:11:42

Unix時間錯誤

2021-03-31 11:52:24

try-catch-fJava代碼

2023-03-10 08:48:29

2024-06-25 10:37:11

2023-05-16 15:32:45

JavaScriptWeb前端工程師

2020-06-15 08:12:51

try catch代碼處理器

2024-11-26 08:55:51

RustPanic

2025-01-16 12:00:00

try-catchfor循環(huán)

2023-11-13 17:01:26

C++編程

2025-03-10 08:10:00

安全賦值運算符ECMAScript編碼

2024-08-26 11:16:55

RustTypeScript語言

2024-11-04 08:20:00

try-catch編程

2024-11-13 01:00:18

asyncawait?編程
點贊
收藏

51CTO技術(shù)棧公眾號