回呼函數是每個前端程式設計師都應該知道的概念之一。回調可用於陣列、計時器函數、promise、事件處理。本文將會解釋回呼函數的概念,同時幫你區分兩種回呼:同步和非同步。
#先寫一個向人打招呼的函數。
只需要建立一個接受 name
參數的函數 greet(name)
。這個函數要回傳打招呼的訊息:
function greet(name) { return `Hello, ${name}!`; } greet('Cristina'); // => 'Hello, Cristina!'
如果向很多人打招呼該怎麼辦?可以用特殊的陣列方法 array.map()
可以實作:
const persons = ['Cristina', 'Ana']; const messages = persons.map(greet); messages; // => ['Hello, Cristina!', 'Hello, Ana!']
persons.map(greet)
可以取得persons
陣列的所有元素,並分別以每個元素作為呼叫參數來呼叫greet()
函數:`greet('Cristina')
, greet('Ana')
。
有趣的是 persons.map(greet)
方法可以接受 greet()
函數作為參數。這樣 greet()
就變成了回呼函數。
persons.map(greet)
是用另一個函數作為參數的函數,因此稱為高階函數。
回呼函數作為高階函數的參數,高階函數透過呼叫回呼函數來執行操作。
重要的是高階函數負責呼叫回調,並為其提供正確的參數。
前面的範例中,高階函數persons.map(greet)
負責呼叫 greet()
函數,並且分別將陣列中所有的元素'Cristina'
和Ana '
作為參數。
這就為識別回呼提供了一條簡單的規則。如果你定義了一個函數,並將其作參數提供給另一個函數的話,那麼這就創建了一個回呼。
你可以自己寫一個使用回呼的高階函數。以下是array.map()
方法的等效版本:
function map(array, callback) { const mappedArray = []; for (const item of array) { mappedArray.push( callback(item) ); } return mappedArray; } function greet(name) { return `Hello, ${name}!`; } const persons = ['Cristina', 'Ana']; const messages = map(persons, greet);messages; // => ['Hello, Cristina!', 'Hello, Ana!']
map(array, callback)
是高階函數,因為它用回呼函數作為參數,然後在其主體內部呼叫該回呼函數:callback(item)
。
注意,常規函數(以關鍵字 function
定義)或箭頭函數(以粗箭頭 =>
定義)同樣可以作為回調使用。
回呼的呼叫方式有兩種:同步和非同步回呼。
同步回呼是「阻塞」的:高階函數直到回呼函數完成後才繼續執行。
例如,呼叫 map()
和 greet()
函數。
function map(array, callback) { console.log('map() starts'); const mappedArray = []; for (const item of array) { mappedArray.push(callback(item)) } console.log('map() completed'); return mappedArray; } function greet(name) { console.log('greet() called'); return `Hello, ${name}!`; } const persons = ['Cristina']; map(persons, greet); // logs 'map() starts' // logs 'greet() called' // logs 'map() completed'
其中 greet()
是同步回呼。
同步回呼的步驟:
高階函數開始執行:'map() starts'
'greet() called'
'map() completed'
array.map(callback),
array.forEach(callback),
array.find(callback ),
array.filter(callback),
array.reduce(callback, init)
// Examples of synchronous callbacks on arrays const persons = ['Ana', 'Elena']; persons.forEach( function callback(name) { console.log(name); } ); // logs 'Ana' // logs 'Elena' const nameStartingA = persons.find( function callback(name) { return name[0].toLowerCase() === 'a'; } ); nameStartingA; // => 'Ana' const countStartingA = persons.reduce( function callback(count, name) { const startsA = name[0].toLowerCase() === 'a'; return startsA ? count + 1 : count; }, 0 ); countStartingA; // => 1
string.replace( callback) 方法也能接受同步執行的回呼:
// Examples of synchronous callbacks on strings const person = 'Cristina'; // Replace 'i' with '1' person.replace(/./g, function(char) { return char.toLowerCase() === 'i' ? '1' : char; } ); // => 'Cr1st1na'
later() 函數的執行延遲了2 秒:
console.log('setTimeout() starts'); setTimeout(function later() { console.log('later() called'); }, 2000); console.log('setTimeout() completed'); // logs 'setTimeout() starts' // logs 'setTimeout() completed' // logs 'later() called' (after 2 seconds)
later() 是一個非同步回調,因為
setTimeout(later,2000) 啟動並完成了執行,但是
later() 在2 秒後執行。
'setTimeout()starts'
'setTimeout() completed'
'later( ) called'
setTimeout(function later() { console.log('2 seconds have passed!'); }, 2000); // After 2 seconds logs '2 seconds have passed!' setInterval(function repeat() { console.log('Every 2 seconds'); }, 2000); // Each 2 seconds logs 'Every 2 seconds!'
const myButton = document.getElementById('myButton'); myButton.addEventListener('click', function handler() { console.log('Button clicked!'); }); // Logs 'Button clicked!' when the button is clicked
async 會建立一個非同步函數:
async function fetchUserNames() { const resp = await fetch('https://api.github.com/users?per_page=5'); const users = await resp.json(); const names = users.map(({ login }) => login); console.log(names); }
fetchUserNames()
是异步的,因为它以 async
为前缀。函数 await fetch('https://api.github.com/users?per_page=5')
从 GitHub 上获取前5个用户 。然后从响应对象中提取 JSON 数据:await resp.json()
。
异步函数是 promise 之上的语法糖。当遇到表达式 await <promise>
(调用 fetch()
会返回一个promise)时,异步函数会暂停执行,直到 promise 被解决。
异步回调函数和异步函数是不同的两个术语。
异步回调函数由高阶函数以非阻塞方式执行。但是异步函数在等待 promise(await <promise>
)解析时会暂停执行。
但是你可以把异步函数用作异步回调!
让我们把异步函数 fetch UserNames()
设为异步回调,只需单击按钮即可调用:
const button = document.getElementById('fetchUsersButton'); button.addEventListener('click', fetchUserNames);
回调是一个可以作为参数传给另一个函数(高阶函数)执行的函数。
回调函数有两种:同步和异步。
同步回调是阻塞的。
异步回调是非阻塞的。
【相关推荐:javascript学习教程】
以上是深入解析JavaScript中的回呼函數(同步與非同步)的詳細內容。更多資訊請關注PHP中文網其他相關文章!