Kodokon kodokon.com

การเขียนแบบอะซิงโครนัสในทางปฏิบัติ

ใช้ async/await ในเราต์ของคุณ และจัดการข้อผิดพลาดแบบอะซิงโครนัสโดยไม่ทำให้เซิร์ฟเวอร์ล่ม

10 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

เราต์จริง ๆ ของคุณไม่ได้คืนอาร์เรย์ในหน่วยความจำ แต่จะอ่านไฟล์ คิวรีฐานข้อมูล และเรียก API อื่น ๆ การดำเนินการเหล่านี้ล้วนเป็นแบบอะซิงโครนัสทั้งสิ้น คุณรู้จัก async/await มาตั้งแต่คอร์ส JavaScript แล้ว มาดูวิธีใช้มันอย่างสะอาดในเซิร์ฟเวอร์กัน ข่าวดีอย่างแรกคือ ฟังก์ชันของเราต์สามารถเป็น async ได้

JAVASCRIPT
import express from "express";
import { readFile } from "node:fs/promises";

const app = express();

app.get("/report", async (req, res) => {
  const raw = await readFile("report.json", "utf8");
  res.json(JSON.parse(raw));
});

app.listen(3000);

โค้ดนี้ทำงานได้... ตราบเท่าที่ report.json มีอยู่จริง หากการอ่านล้มเหลว โพรมิสจะถูกปฏิเสธ (rejected) และขึ้นอยู่กับเวอร์ชันของ Express คำขออาจค้างอยู่โดยไม่มีการตอบกลับ หรือข้อผิดพลาดอาจรั่วไหลไปในล็อก กฎของมืออาชีพคือ ทุกเราต์แบบอะซิงโครนัสต้องจัดการข้อผิดพลาดของตัวเอง ด้วย try/catch และ next(error)

JAVASCRIPT
app.get("/report", async (req, res, next) => {
  try {
    const raw = await readFile("report.json", "utf8");
    res.json(JSON.parse(raw));
  } catch (error) {
    next(error);
  }
});

next(error) จะส่งข้อผิดพลาดต่อไปยัง ตัวจัดการข้อผิดพลาดส่วนกลาง (centralized error handler) ซึ่งเป็นมิดเดิลแวร์ที่มีสี่พารามิเตอร์ ประกาศไว้หลังเราต์ทั้งหมด จุดเดียวสำหรับบันทึกล็อกและจัดรูปแบบข้อผิดพลาด นี่คือสิ่งที่แยก API ที่ดูแลรักษาได้ ออกจากโปรเจกต์ทำเล่นสุดสัปดาห์

JAVASCRIPT
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({
    error: "Internal server error"
  });
});
อยู่ท้ายสุดเสมอ หลังเราต์ทั้งหมด

ปฏิกิริยาสุดท้ายที่ต้องฝึกให้ติดคือ การทำงานแบบขนาน (parallelism) การดำเนินการอิสระสองอย่างที่ต่อกันด้วย await สองตัวจะทำงานทีละอันตามลำดับ ส่วน Promise.all จะเริ่มทั้งสองพร้อมกัน เวลาตอบสนองจึงเท่ากับตัวที่ช้าที่สุด ไม่ใช่ผลรวมของทั้งสอง

JAVASCRIPT
async function fetchUsers() {
  return [{ id: 1, name: "Ada" }];
}

async function fetchTasks() {
  return [{ id: 1, title: "Review the code" }];
}

app.get("/dashboard", async (req, res, next) => {
  try {
    const [users, tasks] = await Promise.all([
      fetchUsers(),
      fetchTasks()
    ]);
    res.json({ users, tasks });
  } catch (error) {
    next(error);
  }
});

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. ในเราต์แบบ async คุณจะทำอย่างไรกับข้อผิดพลาดที่ถูกดักจับด้วย catch?
    • บันทึกล็อกด้วย console.log แล้วทำงานต่อ
    • เรียก next(error) เพื่อส่งต่อไปยังตัวจัดการข้อผิดพลาด
    • ลองส่งคำขอใหม่โดยอัตโนมัติ
  2. การเรียกอะซิงโครนัสอิสระสองครั้งต้องทำงานให้เร็วที่สุดเท่าที่จะเป็นไปได้ คุณจะเลือกแนวทางใด?
    • ต่อกันด้วย await สองตัวเรียงลำดับกัน
    • เริ่มทั้งสองพร้อมกันด้วย Promise.all
    • ใช้ setTimeout คั่นระหว่างทั้งสอง
  3. Express รู้จักมิดเดิลแวร์สำหรับจัดการข้อผิดพลาดได้อย่างไร?
    • จากลายเซ็นที่มีสี่พารามิเตอร์ โดยมี err เป็นตัวแรก
    • จากชื่อฟังก์ชัน ซึ่งต้องเป็น errorHandler
    • จากตำแหน่งที่อยู่บนสุดของไฟล์