偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

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

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

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

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

我在說(shuō)什么? Go 和 Rust 的共同點(diǎn)是什么? 錯(cuò)誤。 對(duì)我來(lái)說(shuō)最突出的事情。 更具體地說(shuō),這些語(yǔ)言如何處理它們。

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

讓我們來(lái)看看它們。 我們不會(huì)深入研究每種語(yǔ)言; 我們想知道一般方法。

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

給自己五秒鐘的時(shí)間來(lái)查看下面的代碼并回答為什么我們需要將其包裝在 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 方法仍然會(huì)拋出錯(cuò)誤。 response.ok 僅“捕獲”4xx 和 5xx 網(wǎng)絡(luò)錯(cuò)誤。 但當(dāng)網(wǎng)絡(luò)本身出現(xiàn)故障時(shí),就會(huì)拋出錯(cuò)誤。

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

所以第一個(gè)問(wèn)題是,就我個(gè)人而言,我認(rèn)為這是有史以來(lái)最大的 JavaScript 問(wèn)題:我們不知道什么會(huì)引發(fā)錯(cuò)誤。 從 JavaScript 錯(cuò)誤的角度來(lái)看,它與以下內(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;
}

沒(méi)有錯(cuò)誤,即使這可能會(huì)破壞您的應(yīng)用程序。

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

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

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

我們正在嘗試打開一個(gè)返回文件或錯(cuò)誤的文件。 您會(huì)經(jīng)??吹竭@種情況,主要是因?yàn)槲覀冎滥男┖瘮?shù)總是返回錯(cuò)誤。 你永遠(yuǎn)不會(huì)錯(cuò)過(guò)任何一個(gè)。 這是將錯(cuò)誤視為值的第一個(gè)示例。 您指定哪個(gè)函數(shù)可以返回它們,您返回它們,您分配它們,您檢查它們,您使用它們。

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

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),
};

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

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

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

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

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

好吧,是時(shí)候說(shuō)實(shí)話了; 我撒了一點(diǎn)謊。 我們不能讓 TypeScript 錯(cuò)誤像 Go / Rust 那樣工作。 這里的限制因素是語(yǔ)言本身; 它沒(méi)有合適的工具來(lái)做到這一點(diǎn)。

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

從這個(gè)開始:

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

這里沒(méi)什么特別的,只是一個(gè)簡(jiǎn)單的泛型類型。 但這個(gè)小寶貝可以完全改變代碼。 您可能會(huì)注意到,這里最大的區(qū)別是我們要么返回?cái)?shù)據(jù),要么返回錯(cuò)誤。 聽起來(lái)很熟悉?

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

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" };
  }
}

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

舊的(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)

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

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

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

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

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

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

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

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

我們不能刪除 !response.success 檢查,因?yàn)榉駝t,response.data 將不存在。

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

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

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

以下是使用 SvelteKit FormAction 的真實(shí)示例:

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;

這里有幾點(diǎn)需要指出:

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

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

謝謝閱讀。

附: 看起來(lái)很相似?

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é)任編輯:姜華 來(lái)源: 今日頭條
相關(guān)推薦

2020-09-27 07:48:40

不用try catch

2025-04-29 08:05:00

JavaScript錯(cuò)誤處理開發(fā)

2020-08-24 13:35:59

trycatchJava

2021-01-05 07:54:55

事項(xiàng)trycatch

2025-07-03 07:05:00

JavaScriptPromise代碼

2017-11-02 15:26:10

JavaScriptasync錯(cuò)誤

2024-05-24 08:59:15

2009-12-02 19:56:33

PHP中try{}ca

2009-02-17 09:11:42

Unix時(shí)間錯(cuò)誤

2021-03-31 11:52:24

try-catch-fJava代碼

2023-03-10 08:48:29

2024-11-26 08:55:51

RustPanic

2024-06-25 10:37:11

2023-05-16 15:32:45

JavaScriptWeb前端工程師

2020-06-15 08:12:51

try catch代碼處理器

2025-01-16 12:00:00

try-catchfor循環(huán)

2023-11-13 17:01:26

C++編程

2025-06-25 08:15:00

JavaScrip異步編程代碼

2025-03-10 08:10:00

安全賦值運(yùn)算符ECMAScript編碼

2024-08-26 11:16:55

RustTypeScript語(yǔ)言
點(diǎn)贊
收藏

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