cari
Rumahhujung hadapan webtutorial jsCara Mengelakkan Perangkap Berulir Tunggal dalam JavaScript

How to Avoid the Single-Threaded Trap in JavaScript

JavaScript sering digambarkan sebagai benang tunggal, yang bermaksud ia melaksanakan satu tugas pada satu masa. Tetapi adakah ini membayangkan bahawa setiap bahagian kod berjalan dalam pengasingan sepenuhnya, tanpa keupayaan untuk mengendalikan tugas lain sambil menunggu operasi tak segerak seperti respons HTTP atau permintaan pangkalan data? Jawapannya tidak! Malah, gelung acara dan janji JavaScript membenarkannya mengendalikan tugas tak segerak dengan cekap manakala kod lain terus dijalankan.

Sebenarnya javascript memang satu benang, walau bagaimanapun, salah faham bagaimana ini berfungsi boleh membawa kepada perangkap biasa. Satu perangkap sedemikian ialah mengurus operasi tak segerak seperti permintaan API, terutamanya apabila cuba mengawal akses kepada sumber yang dikongsi tanpa menyebabkan keadaan perlumbaan. Mari kita terokai contoh dunia sebenar dan lihat bagaimana pelaksanaan yang lemah boleh membawa kepada pepijat yang serius.

Saya menemui pepijat dalam aplikasi yang memerlukan log masuk ke perkhidmatan hujung belakang untuk mengemas kini data. Selepas log masuk, apl akan menerima token akses dengan tarikh tamat tempoh yang ditentukan. Setelah tarikh tamat tempoh ini berlalu, kami perlu mengesahkan semula sebelum membuat sebarang permintaan baharu pada titik akhir kemas kini. Cabaran timbul kerana titik akhir log masuk telah dikurangkan kepada maksimum satu permintaan setiap lima minit, manakala titik akhir kemas kini perlu dipanggil dengan lebih kerap dalam tetingkap lima minit yang sama. Adalah penting untuk logik berfungsi dengan betul, namun titik akhir log masuk kadang-kadang dicetuskan beberapa kali dalam selang lima minit, menyebabkan titik akhir kemas kini gagal berfungsi. Walaupun ada kalanya segala-galanya berfungsi seperti yang dijangkakan, pepijat yang terputus-putus ini menimbulkan risiko yang lebih serius, kerana ia boleh memberikan rasa selamat yang salah pada mulanya, menjadikannya kelihatan seperti sistem beroperasi dengan betul._

Untuk menggambarkan contoh ini, kami menggunakan apl NestJS yang sangat asas yang merangkumi perkhidmatan berikut:

  • AppService: Bertindak sebagai pengawal untuk mensimulasikan dua varian— versi buruk, yang kadangkala berfungsi dan kadangkala tidak, dan versi yang baik, yang dijamin sentiasa berfungsi dengan betul.
  • BadAuthenticationService: Pelaksanaan untuk versi buruk.
  • GoodAuthenticationService: Pelaksanaan untuk versi yang baik.
  • AbstractAuthenticationService: Kelas yang bertanggungjawab untuk mengekalkan keadaan kongsi antara GoodAuthenticationService dan BadAuthenticationService.
  • LoginThrottleService: Kelas yang menyerupai mekanisme pendikit titik akhir log masuk untuk perkhidmatan hujung belakang.
  • MockHttpService: Kelas yang membantu mensimulasikan permintaan HTTP.
  • MockAwsCloudwatchApiService: Mensimulasikan panggilan API ke sistem pengelogan AWS CloudWatch.

Saya tidak akan menunjukkan kod untuk semua kelas ini di sini; anda boleh menemuinya terus dalam repositori GitHub. Sebaliknya, saya akan memberi tumpuan khusus pada logik dan perkara yang perlu diubah agar ia berfungsi dengan betul.

Pendekatan Buruk:

@Injectable()
export class BadAuthenticationService extends AbstractAuthenticationService {
  async loginToBackendService() {
    this.loginInProgress = true; // this is BAD, we are inside a promise, it's asynchronous. it's not synchronous, javascript can execute it whenever it wants

    try {
      const response = await firstValueFrom(
        this.httpService.post(`https://backend-service.com/login`, {
          password: 'password',
        }),
      );

      return response;
    } finally {
      this.loginInProgress = false;
    }
  }

  async sendProtectedRequest(route: string, data?: unknown) {
    if (!this.accessToken) {
      if (this.loginInProgress) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        return this.sendProtectedRequest(route, data);
      }

      try {
        await this.awsCloudwatchApiService.logLoginCallAttempt();
        const { data: loginData } = await this.loginToBackendService();
        this.accessToken = loginData.accessToken;
      } catch (e: any) {
        console.error(e?.response?.data);
        throw e;
      }
    }

    try {
      const response = await firstValueFrom(
        this.httpService.post(`https://backend-service.com${route}`, data, {
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
          },
        }),
      );

      return response;
    } catch (e: any) {
      if (e?.response?.data?.statusCode === 401) {
        this.accessToken = null;
        return this.sendProtectedRequest(route, data);
      }
      console.error(e?.response?.data);
      throw e;
    }
  }
}

Mengapa Ini Adalah Pendekatan Buruk:

Dalam BadAuthenticationService, kaedah loginToBackendService menetapkan this.loginInProgress kepada benar apabila memulakan permintaan log masuk. Walau bagaimanapun, kerana kaedah ini tidak segerak, ia tidak menjamin bahawa status log masuk akan dikemas kini dengan serta-merta. Ini boleh membawa kepada berbilang panggilan serentak ke titik akhir log masuk dalam had pendikit.
Apabila sendProtectedRequest mengesan bahawa token akses tiada, ia menyemak sama ada log masuk sedang dijalankan. Jika ya, fungsi menunggu sebentar dan kemudian cuba semula. Walau bagaimanapun, jika permintaan lain masuk pada masa ini, ia boleh mencetuskan percubaan log masuk tambahan. Ini boleh membawa kepada berbilang panggilan ke titik akhir log masuk, yang dikurangkan untuk membenarkan hanya satu panggilan setiap minit. Akibatnya, titik akhir kemas kini mungkin gagal seketika, menyebabkan tingkah laku yang tidak dapat diramalkan dan rasa selamat yang salah apabila sistem kelihatan berfungsi dengan baik pada masa-masa tertentu.

Ringkasnya, masalahnya terletak pada pengendalian operasi tak segerak yang tidak betul, yang membawa kepada keadaan perlumbaan berpotensi yang boleh memecahkan logik aplikasi.

Pendekatan yang baik:

@Injectable()
export class GoodAuthenticationService extends AbstractAuthenticationService {
  async loginToBackendService() {
    try {
      const response = await firstValueFrom(
        this.httpService.post(`https://backend-service.com/login`, {
          password: 'password',
        }),
      );

      return response;
    } finally {
      this.loginInProgress = false;
    }
  }

  async sendProtectedRequest(route: string, data?: unknown) {
    if (!this.accessToken) {
      if (this.loginInProgress) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        return this.sendProtectedRequest(route, data);
      }

      // Critical: Set the flag before ANY promise call
      this.loginInProgress = true;

      try {
        await this.awsCloudwatchApiService.logLoginCallAttempt();
        const { data: loginData } = await this.loginToBackendService();
        this.accessToken = loginData.accessToken;
      } catch (e: any) {
        console.error(e?.response?.data);
        throw e;
      }
    }

    try {
      const response = await firstValueFrom(
        this.httpService.post(`https://backend-service.com${route}`, data, {
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
          },
        }),
      );

      return response;
    } catch (e: any) {
      if (e?.response?.data?.statusCode === 401) {
        this.accessToken = null;
        return this.sendProtectedRequest(route, data);
      }
      console.error(e?.response?.data);
      throw e;
    }
  }
}

Mengapa Ini Adalah Pendekatan yang Baik:

Dalam GoodAuthenticationService, kaedah loginToBackendService distrukturkan untuk mengendalikan logik log masuk dengan cekap. Peningkatan utama ialah pengurusan bendera loginInProgress. Ia ditetapkan selepas mengesahkan bahawa token akses tiada dan sebelum sebarang operasi tak segerak bermula. Ini memastikan bahawa sebaik sahaja percubaan log masuk dimulakan, tiada panggilan log masuk lain boleh dibuat secara serentak, dengan berkesan menghalang berbilang permintaan ke titik akhir log masuk pendikit.

Arahan Demo

Klon Repositori:

@Injectable()
export class BadAuthenticationService extends AbstractAuthenticationService {
  async loginToBackendService() {
    this.loginInProgress = true; // this is BAD, we are inside a promise, it's asynchronous. it's not synchronous, javascript can execute it whenever it wants

    try {
      const response = await firstValueFrom(
        this.httpService.post(`https://backend-service.com/login`, {
          password: 'password',
        }),
      );

      return response;
    } finally {
      this.loginInProgress = false;
    }
  }

  async sendProtectedRequest(route: string, data?: unknown) {
    if (!this.accessToken) {
      if (this.loginInProgress) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        return this.sendProtectedRequest(route, data);
      }

      try {
        await this.awsCloudwatchApiService.logLoginCallAttempt();
        const { data: loginData } = await this.loginToBackendService();
        this.accessToken = loginData.accessToken;
      } catch (e: any) {
        console.error(e?.response?.data);
        throw e;
      }
    }

    try {
      const response = await firstValueFrom(
        this.httpService.post(`https://backend-service.com${route}`, data, {
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
          },
        }),
      );

      return response;
    } catch (e: any) {
      if (e?.response?.data?.statusCode === 401) {
        this.accessToken = null;
        return this.sendProtectedRequest(route, data);
      }
      console.error(e?.response?.data);
      throw e;
    }
  }
}

Pasang Ketergantungan yang Diperlukan:

@Injectable()
export class GoodAuthenticationService extends AbstractAuthenticationService {
  async loginToBackendService() {
    try {
      const response = await firstValueFrom(
        this.httpService.post(`https://backend-service.com/login`, {
          password: 'password',
        }),
      );

      return response;
    } finally {
      this.loginInProgress = false;
    }
  }

  async sendProtectedRequest(route: string, data?: unknown) {
    if (!this.accessToken) {
      if (this.loginInProgress) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        return this.sendProtectedRequest(route, data);
      }

      // Critical: Set the flag before ANY promise call
      this.loginInProgress = true;

      try {
        await this.awsCloudwatchApiService.logLoginCallAttempt();
        const { data: loginData } = await this.loginToBackendService();
        this.accessToken = loginData.accessToken;
      } catch (e: any) {
        console.error(e?.response?.data);
        throw e;
      }
    }

    try {
      const response = await firstValueFrom(
        this.httpService.post(`https://backend-service.com${route}`, data, {
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
          },
        }),
      );

      return response;
    } catch (e: any) {
      if (e?.response?.data?.statusCode === 401) {
        this.accessToken = null;
        return this.sendProtectedRequest(route, data);
      }
      console.error(e?.response?.data);
      throw e;
    }
  }
}

Jalankan Aplikasi:

git clone https://github.com/zenstok/nestjs-singlethread-trap.git

Mensimulasikan permintaan:

  • Untuk mensimulasikan dua permintaan dengan versi buruk, hubungi:
cd nestjs-singlethread-trap
npm install

Untuk mensimulasikan dua permintaan dengan versi yang baik, hubungi:

npm run start

Kesimpulan: Mengelakkan Perangkap Satu Threaded JavaScript

Walaupun JavaScript adalah satu benang, ia boleh mengendalikan tugas tak segerak seperti permintaan HTTP dengan cekap menggunakan janji dan gelung acara. Walau bagaimanapun, pengendalian janji ini yang tidak betul, terutamanya dalam senario yang melibatkan sumber dikongsi (seperti token), boleh membawa kepada keadaan perlumbaan dan tindakan pendua.
Perkara utama ialah menyegerakkan tindakan tak segerak seperti log masuk untuk mengelakkan perangkap sedemikian. Sentiasa pastikan kod anda mengetahui proses yang sedang berjalan dan mengendalikan permintaan dengan cara yang menjamin penjujukan yang betul, walaupun semasa JavaScript melakukan pelbagai tugas di belakang tabir.

Jika anda belum menyertai Kelab Arnab Byte, kini adalah peluang anda untuk memasuki komuniti peminat perisian, pengasas teknologi dan pengasas bukan teknologi yang berkembang maju. Bersama-sama, kami berkongsi pengetahuan, belajar daripada satu sama lain, dan bersedia untuk membina permulaan besar seterusnya. Sertai kami hari ini dan jadilah sebahagian daripada perjalanan yang menarik ke arah inovasi dan pertumbuhan!

Atas ialah kandungan terperinci Cara Mengelakkan Perangkap Berulir Tunggal dalam JavaScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Enjin JavaScript: Membandingkan PelaksanaanEnjin JavaScript: Membandingkan PelaksanaanApr 13, 2025 am 12:05 AM

Enjin JavaScript yang berbeza mempunyai kesan yang berbeza apabila menguraikan dan melaksanakan kod JavaScript, kerana prinsip pelaksanaan dan strategi pengoptimuman setiap enjin berbeza. 1. Analisis leksikal: Menukar kod sumber ke dalam unit leksikal. 2. Analisis Tatabahasa: Menjana pokok sintaks abstrak. 3. Pengoptimuman dan Penyusunan: Menjana kod mesin melalui pengkompil JIT. 4. Jalankan: Jalankan kod mesin. Enjin V8 mengoptimumkan melalui kompilasi segera dan kelas tersembunyi, Spidermonkey menggunakan sistem kesimpulan jenis, menghasilkan prestasi prestasi yang berbeza pada kod yang sama.

Beyond the Browser: JavaScript di dunia nyataBeyond the Browser: JavaScript di dunia nyataApr 12, 2025 am 12:06 AM

Aplikasi JavaScript di dunia nyata termasuk pengaturcaraan sisi pelayan, pembangunan aplikasi mudah alih dan Internet of Things Control: 1. Pengaturcaraan sisi pelayan direalisasikan melalui node.js, sesuai untuk pemprosesan permintaan serentak yang tinggi. 2. Pembangunan aplikasi mudah alih dijalankan melalui reaktnatif dan menyokong penggunaan silang platform. 3. Digunakan untuk kawalan peranti IoT melalui Perpustakaan Johnny-Five, sesuai untuk interaksi perkakasan.

Membina aplikasi SaaS Multi-penyewa dengan Next.js (Integrasi Backend)Membina aplikasi SaaS Multi-penyewa dengan Next.js (Integrasi Backend)Apr 11, 2025 am 08:23 AM

Saya membina aplikasi SaaS multi-penyewa berfungsi (aplikasi edTech) dengan alat teknologi harian anda dan anda boleh melakukan perkara yang sama. Pertama, apakah aplikasi SaaS multi-penyewa? Aplikasi SaaS Multi-penyewa membolehkan anda melayani beberapa pelanggan dari Sing

Cara Membina Aplikasi SaaS Multi-Tenant dengan Next.js (Integrasi Frontend)Cara Membina Aplikasi SaaS Multi-Tenant dengan Next.js (Integrasi Frontend)Apr 11, 2025 am 08:22 AM

Artikel ini menunjukkan integrasi frontend dengan backend yang dijamin oleh permit, membina aplikasi edtech SaaS yang berfungsi menggunakan Next.Js. Frontend mengambil kebenaran pengguna untuk mengawal penglihatan UI dan memastikan permintaan API mematuhi dasar peranan

JavaScript: meneroka serba boleh bahasa webJavaScript: meneroka serba boleh bahasa webApr 11, 2025 am 12:01 AM

JavaScript adalah bahasa utama pembangunan web moden dan digunakan secara meluas untuk kepelbagaian dan fleksibiliti. 1) Pembangunan front-end: Membina laman web dinamik dan aplikasi satu halaman melalui operasi DOM dan kerangka moden (seperti React, Vue.js, sudut). 2) Pembangunan sisi pelayan: Node.js menggunakan model I/O yang tidak menyekat untuk mengendalikan aplikasi konkurensi tinggi dan masa nyata. 3) Pembangunan aplikasi mudah alih dan desktop: Pembangunan silang platform direalisasikan melalui reaktnatif dan elektron untuk meningkatkan kecekapan pembangunan.

Evolusi JavaScript: Trend Semasa dan Prospek Masa DepanEvolusi JavaScript: Trend Semasa dan Prospek Masa DepanApr 10, 2025 am 09:33 AM

Trend terkini dalam JavaScript termasuk kebangkitan TypeScript, populariti kerangka dan perpustakaan moden, dan penerapan webassembly. Prospek masa depan meliputi sistem jenis yang lebih berkuasa, pembangunan JavaScript, pengembangan kecerdasan buatan dan pembelajaran mesin, dan potensi pengkomputeran IoT dan kelebihan.

Demystifying JavaScript: Apa yang berlaku dan mengapa pentingDemystifying JavaScript: Apa yang berlaku dan mengapa pentingApr 09, 2025 am 12:07 AM

JavaScript adalah asas kepada pembangunan web moden, dan fungsi utamanya termasuk pengaturcaraan yang didorong oleh peristiwa, penjanaan kandungan dinamik dan pengaturcaraan tak segerak. 1) Pengaturcaraan yang didorong oleh peristiwa membolehkan laman web berubah secara dinamik mengikut operasi pengguna. 2) Penjanaan kandungan dinamik membolehkan kandungan halaman diselaraskan mengikut syarat. 3) Pengaturcaraan Asynchronous memastikan bahawa antara muka pengguna tidak disekat. JavaScript digunakan secara meluas dalam interaksi web, aplikasi satu halaman dan pembangunan sisi pelayan, sangat meningkatkan fleksibiliti pengalaman pengguna dan pembangunan silang platform.

Adakah Python atau JavaScript lebih baik?Adakah Python atau JavaScript lebih baik?Apr 06, 2025 am 12:14 AM

Python lebih sesuai untuk sains data dan pembelajaran mesin, manakala JavaScript lebih sesuai untuk pembangunan front-end dan penuh. 1. Python terkenal dengan sintaks ringkas dan ekosistem perpustakaan yang kaya, dan sesuai untuk analisis data dan pembangunan web. 2. JavaScript adalah teras pembangunan front-end. Node.js menyokong pengaturcaraan sisi pelayan dan sesuai untuk pembangunan stack penuh.

See all articles

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
3 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
3 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
3 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Cara Membuka Segala -galanya Di Myrise
4 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌

Alat panas

Penyesuai Pelayan SAP NetWeaver untuk Eclipse

Penyesuai Pelayan SAP NetWeaver untuk Eclipse

Integrasikan Eclipse dengan pelayan aplikasi SAP NetWeaver.

DVWA

DVWA

Damn Vulnerable Web App (DVWA) ialah aplikasi web PHP/MySQL yang sangat terdedah. Matlamat utamanya adalah untuk menjadi bantuan bagi profesional keselamatan untuk menguji kemahiran dan alatan mereka dalam persekitaran undang-undang, untuk membantu pembangun web lebih memahami proses mengamankan aplikasi web, dan untuk membantu guru/pelajar mengajar/belajar dalam persekitaran bilik darjah Aplikasi web keselamatan. Matlamat DVWA adalah untuk mempraktikkan beberapa kelemahan web yang paling biasa melalui antara muka yang mudah dan mudah, dengan pelbagai tahap kesukaran. Sila ambil perhatian bahawa perisian ini

SublimeText3 versi Inggeris

SublimeText3 versi Inggeris

Disyorkan: Versi Win, menyokong gesaan kod!

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

Muat turun versi mac editor Atom

Muat turun versi mac editor Atom

Editor sumber terbuka yang paling popular