狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

JavaScript 21天入門:異步編程 (Asynchronous)

admin
2024年10月24日 20:36 本文熱度 416

今天來聊聊 JavaScript 中的異步編程,篇幅略微有點長。

異步編程是相對高級的內(nèi)容,對于初學(xué)者來說,如果不能完全理解也沒有關(guān)系,后續(xù)可以再來復(fù)習(xí)。做到盡量理解這里面的知識點就好。

同步編程 vs 異步編程

首先,我們來看看什么是同步編程和異步編程。

在同步編程中,代碼是按順序執(zhí)行的。

也就是每一行代碼都會等待前一行代碼執(zhí)行完畢后再執(zhí)行。比如:

console.log('第一步'); console.log('第二步'); console.log('第三步'); 

在這個例子中,輸出的順序是固定的。即,第一步 -> 第二步 -> 第三步。

但在異步編程中,某些操作可以在后臺執(zhí)行,而不會阻塞主線程。

換句話說,輸出的順序和代碼順序不完全一樣。

在處理一些比較耗時的操作,比如如網(wǎng)絡(luò)請求、文件讀取等,有助于提高效率。

console.log('第一步'); setTimeout(() => {   console.log('第二步'); }, 1000); console.log('第三步'); 

在這個例子中,代碼順序和前面一樣,但輸出的順序是:第一步 -> 第三步 -> 第二步。

這是因為 setTimeout 是一個異步操作函數(shù),它不會阻塞主線程,而是會在 1 秒后執(zhí)行回調(diào)函數(shù)。

至于什么是回調(diào)函數(shù),一會兒再細(xì)說。

為什么需要異步編程

所以,異步編程的主要目的是提高程序的效率,避免阻塞主線程。

假如在一個網(wǎng)頁中發(fā)起一個網(wǎng)絡(luò)請求,而這個請求需要幾秒鐘才能完成的話。

如果使用同步編程,整個網(wǎng)頁在這幾秒鐘內(nèi)都會被阻塞,也就是看起來像卡住了一樣,用戶無法進(jìn)行任何操作。

在如今這個網(wǎng)絡(luò)和服務(wù)器處理能力如此強(qiáng)大的情況下,這顯然是不能被接受的。

那有什么方式來解決這個問題呢?

答案就是異步編程。

而異步編程的實現(xiàn),也有幾種不同的方式,一個一個來看。

使用回調(diào)函數(shù)

回調(diào)函數(shù)是最基本的異步編程方式。

它們允許你在異步操作完成后執(zhí)行某些代碼。比如:

//定義函數(shù)fetchData function fetchData(callback) {   setTimeout(() => {     const data = '數(shù)據(jù)加載完成';     callback(data);   }, 2000); }  console.log('開始加載數(shù)據(jù)'); //調(diào)用函數(shù)fetchData fetchData((data) => {   console.log(data); }); console.log('繼續(xù)執(zhí)行其他操作'); 

在這個例子中,我們定義了一個 fetchData 函數(shù),它接受一個回調(diào)函數(shù)作為參數(shù)。

在 2 秒后,回調(diào)函數(shù)會被調(diào)用,表示數(shù)據(jù)加載完成。

輸出的順序是:開始加載數(shù)據(jù) -> 繼續(xù)執(zhí)行其他操作 -> 數(shù)據(jù)加載完成。

使用 Promise

Promise 是另一種處理異步操作的方式。

它可以讓我們更優(yōu)雅地處理異步操作,避免回調(diào)地獄

function fetchData() {   return new Promise((resolve, reject) => {     setTimeout(() => {       const data = '數(shù)據(jù)加載完成';       resolve(data);     }, 2000);   }); }  console.log('開始加載數(shù)據(jù)'); fetchData()   .then((data) => {     console.log(data);   })   .catch((error) => {     console.error(error);   }); console.log('繼續(xù)執(zhí)行其他操作'); 

在這個例子中,我們定義了一個 fetchData 函數(shù),它返回一個 Promise。

在 2 秒后,Promise 會被 resolve,表示數(shù)據(jù)加載完成。

輸出的順序是:開始加載數(shù)據(jù) -> 繼續(xù)執(zhí)行其他操作 -> 數(shù)據(jù)加載完成。

使用 async/await

async 和 await 是基于 Promise 的語法糖,使異步代碼看起來更像同步代碼,更易讀易寫。

function fetchData() {   return new Promise((resolve, reject) => {     setTimeout(() => {       const data = '數(shù)據(jù)加載完成';       resolve(data);     }, 2000);   }); }  async function loadData() {   console.log('開始加載數(shù)據(jù)');   try {     const data = await fetchData();     console.log(data);   } catch (error) {     console.error(error);   }   console.log('繼續(xù)執(zhí)行其他操作'); }  loadData(); 

在這個例子中,我們定義了一個 loadData 異步函數(shù),并在其中使用 await 來等待 fetchData 的結(jié)果。

輸出的順序是:開始加載數(shù)據(jù) -> 數(shù)據(jù)加載完成 -> 繼續(xù)執(zhí)行其他操作。

使用 Web Workers

Web Workers 是另一種處理異步操作的方式,它允許我們在后臺線程中執(zhí)行代碼,而不會阻塞主線程。

JavaScript 是單線程的,這意味著它一次只能執(zhí)行一個任務(wù)。

如果一個任務(wù)耗時較長,整個應(yīng)用程序的響應(yīng)速度就會變慢,甚至?xí)霈F(xiàn)卡頓現(xiàn)象。

Web Workers 允許我們在主線程之外創(chuàng)建獨立的工作線程來處理耗時的任務(wù),從而避免阻塞主線程,提高應(yīng)用程序的性能和用戶體驗。

使用 Web Workers 的具體場景大概有如下:

  1. 處理計算密集型任務(wù):例如復(fù)雜的數(shù)學(xué)計算、圖像處理等,這些任務(wù)可以放在 Web Worker 中執(zhí)行,從而避免阻塞主線程。

  2. 處理大數(shù)據(jù):在處理大量數(shù)據(jù)時,可以將數(shù)據(jù)處理任務(wù)交給 Web Worker,從而保持主線程的流暢運行。

  3. 文件處理:例如讀取和解析大文件,可以使用 Web Worker 來處理文件流,避免主線程卡頓。

  4. WebSocket 消息處理:在處理 WebSocket 消息時,可以使用 Web Worker 來處理接收到的消息,從而提高消息處理的效率。

Web Workers 的使用也是有限制的,如下:

  • 同源限制:Worker 線程執(zhí)行的腳本文件必須與主線程的文件同源。

  • 文件限制:Worker 線程無法讀取本地文件,文件需要通過主線程讀取后再傳輸給 Worker。

  • DOM 操作限制:Worker 線程無法直接操作 DOM 對象,但可以通過消息傳遞與主線程通信。

代碼示例

首先,我們需要創(chuàng)建一個 worker 腳本 worker.js

// worker.js self.onmessage = function (event) {   const result = event.data * 2;   self.postMessage(result); }; 

然后,在主線程中使用這個 worker:

const worker = new Worker('worker.js');  worker.onmessage = function (event) {   console.log('計算結(jié)果:', event.data); };  console.log('發(fā)送數(shù)據(jù)到 worker'); worker.postMessage(10); console.log('繼續(xù)執(zhí)行其他操作'); 

在這個例子中,我們創(chuàng)建了一個 worker,并向它發(fā)送數(shù)據(jù)。

worker 會在后臺線程中處理數(shù)據(jù),并將結(jié)果返回給主線程。

輸出的順序是:發(fā)送數(shù)據(jù)到 worker -> 繼續(xù)執(zhí)行其他操作 -> 計算結(jié)果。

異步迭代器和生成器

異步迭代器和生成器允許我們在異步操作中使用 for...of 循環(huán)。

異步迭代器和生成器使得在不阻塞代碼執(zhí)行的情況下遍歷數(shù)據(jù)或執(zhí)行任務(wù)成為可能。

比如,當(dāng)我們通過網(wǎng)絡(luò)一塊一塊地下載數(shù)據(jù)時,異步迭代器可以讓我們在每次數(shù)據(jù)塊到達(dá)時處理它,而不必等待所有數(shù)據(jù)都下載完畢。

這種方式特別適用于處理流式數(shù)據(jù)或分頁數(shù)據(jù)。

async functionasyncGenerator() {   for (let i = 0; i < 3; i++) {     await new Promise((resolve) => setTimeout(resolve, 1000));     yield i;   } }  (async () => {   for await (let num of asyncGenerator()) {     console.log(num);   } })(); 

在這個例子中,我們定義了一個異步生成器 asyncGenerator,它每秒生成一個數(shù)字。

然后,我們使用 for await...of 循環(huán)來迭代生成器的值。

實際應(yīng)用例子

幾個常見的應(yīng)用場景例子,代碼看不懂目前也沒有關(guān)系,只要明白有這個場景的應(yīng)用目前就足夠了。

1. 處理分頁數(shù)據(jù) 在處理分頁數(shù)據(jù)時,異步迭代器可以幫助我們逐頁獲取數(shù)據(jù)并進(jìn)行處理,而不需要一次性加載所有數(shù)據(jù)。

async functionfetchPages(url) {   let page = 1;   while (true) {     const response = await fetch(`${url}?page=${page}`);     const data = await response.json();     if (data.length === 0) break;     yield data;     page++;   } }  (async () => {   for await (let pageData of fetchPages('https://api.xxx.com/items')) {     console.log(pageData);   } })(); 

2. 處理文件流 異步迭代器可以用于逐行讀取大文件,而不需要一次性將整個文件加載到內(nèi)存中。

const fs = require('fs'); const readline = require('readline');  async functionreadLines(filePath) {   const fileStream = fs.createReadStream(filePath);   const rl = readline.createInterface({     input: fileStream,     crlfDelayInfinity,   });    for await (const line of rl) {     yield line;   } }  (async () => {   for await (let line of readLines('largefile.txt')) {     console.log(line);   } })(); 

3. 處理 WebSocket 消息 在處理 WebSocket 消息時,異步迭代器可以幫助我們逐條處理接收到的消息。

async functionreceiveMessages(socket) {   socket.onmessage = (event) => {     socket.queue.push(event.data);   };   socket.queue = [];    while (true) {     if (socket.queue.length > 0) {       yield socket.queue.shift();     } else {       await new Promise((resolve) => setTimeout(resolve, 100));     }   } }  (async () => {   const socket = new WebSocket('wss://example.com/socket');   for await (let message of receiveMessages(socket)) {     console.log(message);   } })(); 

錯誤處理

在異步編程中,錯誤處理尤為重要,特別是在問題調(diào)查中。

在回調(diào)函數(shù)、Promise 和 async/await 中要考慮處理錯誤,確保代碼的健壯性。

比如在使用 async/await 時,我們可以使用 try...catch 來捕獲錯誤:

async function loadData() {   console.log('開始加載數(shù)據(jù)');   try {     const data = await fetchData();     console.log(data);   } catch (error) {     console.error('加載數(shù)據(jù)時出錯:', error);   }   console.log('繼續(xù)執(zhí)行其他操作'); }  loadData(); 

總結(jié)

  • ?? 盡量使用 Promise 和 async/await 來處理異步操作,因為它們比回調(diào)函數(shù)更易讀易維護(hù)。

  • ?? 在異步編程中,錯誤處理尤為重要。使用 try...catch 塊來捕獲 async/await 中的錯誤,使用 .catch() 方法來處理 Promise 中的錯誤。

  • ?? 對于計算密集型任務(wù),可以使用 Web Workers 在后臺線程中執(zhí)行代碼,避免阻塞主線程。


該文章在 2024/10/28 16:28:52 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運作、調(diào)度、堆場、車隊、財務(wù)費用、相關(guān)報表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點,圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務(wù)都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved