首頁 >web前端 >js教程 >深入解析JavaScript中的回呼函數(同步與非同步)

深入解析JavaScript中的回呼函數(同步與非同步)

青灯夜游
青灯夜游轉載
2022-08-04 10:05:193858瀏覽

回呼函數是每個前端程式設計師都應該知道的概念之一。回調可用於陣列、計時器函數、promise、事件處理。本文將會解釋回呼函數的概念,同時幫你區分兩種回呼:同步和非同步。

深入解析JavaScript中的回呼函數(同步與非同步)

1、回呼函數

#先寫一個向人打招呼的函數。

只需要建立一個接受 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 定義)或箭頭函數(以粗箭頭 => 定義)同樣可以作為回調使用。

2、同步回呼

回呼的呼叫方式有兩種:同步非同步回呼。

同步回呼是「阻塞」的:高階函數直到回呼函數完成後才繼續執行。

例如,呼叫 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'

同步回呼的範例

許多原生JavaScript 類型的方法都會使用同步回呼。

最常用的是array 的方法,例如:

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'

#3、非同步回呼

非同步回呼是「非阻塞的」:高階函數無需等待回調完成即可完成其執行。高階函數可確保稍後在特定事件上執行回呼。

在以下的例子中,

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'

  • # 回呼函數在2 秒後執行:

    '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!'
#DOM 事件偵聽器也非同步呼叫事件處理函數(回呼函數的子類型):

const myButton = document.getElementById('myButton');

myButton.addEventListener('click', function handler() {
  console.log('Button clicked!');
});
// Logs 'Button clicked!' when the button is clicked

#4、非同步回呼函數與非同步函數

在函數定義之前加上特殊關鍵字

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中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除