技術(shù)頻道導航
HTML/CSS
.NET技術(shù)
IIS技術(shù)
PHP技術(shù)
Js/JQuery
Photoshop
Fireworks
服務(wù)器技術(shù)
操作系統(tǒng)
網(wǎng)站運營

贊助商

分類目錄

贊助商

最新文章

搜索

JavaScript回調(diào)函數(shù):同步回調(diào)與異步回調(diào)

作者:admin    時間:2022-6-27 15:51:8    瀏覽:

JavaScript 回調(diào)函數(shù),是將函數(shù)作為參數(shù)傳遞給另一個函數(shù),然后可以在另一個函數(shù)中調(diào)用該函數(shù)?;卣{(diào)有很多好處,它們開辟了很多編程可能性?;卣{(diào)方式也不是唯一的,我們可以同步回調(diào),也可以異步回調(diào),這就是我今天要說的內(nèi)容。

同步回調(diào)

許多人第一次接觸回調(diào)是在他們了解到可以為同一個排序算法提供不同的比較函數(shù)時。例如,當使用Array.prototype.sort()方法對整數(shù)數(shù)組進行排序時,可選參數(shù)是比較函數(shù)compareFn。

let arr1 = [22, 25, 55, 66, 23, 15, 1, 12]
arr1.sort() // arr1按升序排序
// arr1 變成 [1, 12, 15, 22, 23, 25, 55, 66]
let arr2 = […arr1] // 復(fù)制 arr1 到 arr2
arr2.sort((e1, e2)=> e2-e1) // arr2 按降序排序
// arr2 變成 [66, 55, 25, 23, 22, 15, 12, 1 ]
// (e1, e2)=> e2-e1 是比較函數(shù)

這種回調(diào)在大多數(shù)編程語言中都有,達到多態(tài)算法的效果。

上面說明了同步回調(diào)是如何工作的。同步回調(diào)在使用它們的高階函數(shù)內(nèi)部執(zhí)行。當高階函數(shù)完成執(zhí)行時,其回調(diào)參數(shù)的執(zhí)行也完成。由于高階函數(shù)必須等待同步回調(diào)執(zhí)行完成,所以同步回調(diào)也稱為阻塞回調(diào)——回調(diào)的執(zhí)行會阻塞調(diào)用者函數(shù)的執(zhí)行。

JavaScript 中同步回調(diào)的一些其他示例是用于迭代數(shù)組的方法:forEach、map、filterreduce、someevery等。

let arr = [1,2,3,4,5] 
let arrDoubled = arr.map(e=>e+e) 
console.log(arrDoubled) // 輸出 [ 2, 4, 6, 8, 10 ]

異步回調(diào)

如果說同步回調(diào)是實現(xiàn)更大編程靈活性的方法,那么異步回調(diào)是實現(xiàn)更高性能和用戶體驗的方法。

異步回調(diào)的強大之處在于 JavaScript 獨特的運行時模型。JavaScript 是一種單線程語言,也就是說 JavaScript 的執(zhí)行引擎只有一個調(diào)用棧。

那么異步執(zhí)行是如何實現(xiàn)的呢?

神奇之處在于 JavaScript 運行時環(huán)境的 API 處理程序。對于 Web 瀏覽器,API 是 Web API;對于 Node.js,API 是 I/O API。執(zhí)行異步回調(diào)的任務(wù)被放入回調(diào)隊列。

在調(diào)用堆棧中的現(xiàn)有代碼運行完成后,事件循環(huán)(作為 JavaScript 引擎的一部分的進程)將回調(diào)隊列中的回調(diào)帶入執(zhí)行。

一旦執(zhí)行引擎運行回調(diào),它會在下一個回調(diào)開始運行之前再次運行到完成(直到調(diào)用堆棧為空)。調(diào)用堆棧上的代碼的這種運行到完成一直持續(xù)到隊列中的所有回調(diào)都被執(zhí)行為止。

異步方面來自這樣一個事實,即回調(diào)不是在高階函數(shù)中立即執(zhí)行,而是放在回調(diào)隊列中等待輪到它在調(diào)用堆棧上運行。

高階函數(shù)是派發(fā)回調(diào)任務(wù)的函數(shù),而不是運行它的函數(shù)。

使用異步回調(diào)最普遍的例子是使用setTimeOut方法。

console.log("setTimeout 之前") 
setTimeout( 
    ()=>{ console.log("這里是2秒后的結(jié)果") }, 
    2000 

console.log("setTimeout 之后")

第一個參數(shù)是回調(diào)函數(shù),第二個參數(shù)是等待的時間,以毫秒為單位。setTimeout無需等待回調(diào)完成即可返回。

以下是輸出的樣子:

setTimeout 之前
setTimeout 之后
這里是2秒后的結(jié)果

異步回調(diào)機制的好處

異步回調(diào)機制的好處是所有同步代碼都不會被異步事件阻塞。異步事件(例如對遠程服務(wù)器的 AJAX 請求)可能需要一些時間才能運行。通過異步回調(diào),Web 應(yīng)用程序可以更流暢地運行且響應(yīng)速度更快。

示例:同步回調(diào)轉(zhuǎn)換為異步回調(diào)

同步回調(diào)

console.log('start');

function getGreeting(name, cb) {
  cb(`Hello ${name}`);
}

console.log('before getGreeting');

getGreeting('WebKaka', (greeting) => {
  console.log(greeting);
});

console.log('end');

輸出

start
before getGreeting
Hello WebKaka
end

該程序從頂部開始,并在到達底部時順序執(zhí)行每一行。

異步回調(diào)

我們可以把上面的例子改為異步回調(diào)。

console.log('start');

function getGreetingAsync(name, cb) {
   setTimeout(() => {
     cb(`Hello ${name}`);
   }, 0);
}

console.log('before getGreetingAsync');

getGreetingAsync('WebKaka', (greeting) => {
  console.log(greeting);
});

console.log('end');

輸出

start
before getGreetingAsync
end
Hello WebKaka

通過添加 setTimeout,我們將回調(diào)函數(shù)的執(zhí)行推遲到稍后的時間點?;卣{(diào)函數(shù)只有在程序從上到下執(zhí)行完代碼后才會運行(即使延遲為0ms)。

同步回調(diào)和異步回調(diào)之間的主要區(qū)別在于同步回調(diào)立即執(zhí)行,而異步回調(diào)的執(zhí)行推遲到稍后的時間點。

如何判斷回調(diào)是同步還是異步?

回調(diào)是同步執(zhí)行還是異步執(zhí)行取決于調(diào)用它的函數(shù)。如果函數(shù)是異步的,那么回調(diào)也是異步的。

異步函數(shù)通常是執(zhí)行網(wǎng)絡(luò)請求、等待 I/O 操作(如鼠標單擊)、與文件系統(tǒng)交互或向數(shù)據(jù)庫發(fā)送查詢的函數(shù)。這些函數(shù)的共同點是它們與當前程序之外的東西進行交互,并且你的應(yīng)用程序一直等待直到響應(yīng)返回。

相反,同步回調(diào)在程序的當前上下文中執(zhí)行,與外界沒有交互。你會在函數(shù)式編程中找到同步回調(diào),例如,為集合中的每個項目調(diào)用回調(diào)(例如.filter()、.map().reduce()等)。JavaScript 語言中的大多數(shù)原型方法都是同步的。

如果你不確定一個回調(diào)函數(shù)是同步執(zhí)行還是異步執(zhí)行,你可以在回調(diào)內(nèi)部和之后添加console.log語句,看看哪個先打印。

總結(jié)

本文介紹了JavaScript回調(diào)函數(shù):同步回調(diào)與異步回調(diào)。無論是同步回調(diào)還是異步回調(diào),都有各自的好處,在使用時需根據(jù)具體情況而選擇采用何種編程方式。

相關(guān)文章

x
  • 站長推薦
/* 左側(cè)顯示文章內(nèi)容目錄 */