Maison > interface Web > js tutoriel > le corps du texte

Analyse approfondie des fonctions de rappel en JavaScript (synchrones et asynchrones)

青灯夜游
Libérer: 2022-08-04 10:05:19
avant
3823 Les gens l'ont consulté

La fonction de rappel est l'un des concepts que tout programmeur front-end devrait connaître. Les rappels peuvent être utilisés dans les tableaux, les fonctions de minuterie, les promesses et la gestion des événements. Cet article expliquera le concept des fonctions de rappel et vous aidera à distinguer deux types de rappels : synchrones et asynchrones.

Analyse approfondie des fonctions de rappel en JavaScript (synchrones et asynchrones)

1. Fonction de rappel

Écrivez d'abord une fonction pour saluer les gens.

Créez simplement une fonction greet(name) qui accepte le paramètre name. Cette fonction devrait renvoyer le message de bienvenue : name 参数的函数 greet(name)。这个函数应返回打招呼的消息:

function greet(name) {
  return `Hello, ${name}!`;
}

greet('Cristina'); // => 'Hello, Cristina!'
Copier après la connexion

如果向很多人打招呼该怎么办?可以用特殊的数组方法  array.map() 可以实现:

const persons = ['Cristina', 'Ana'];

const messages = persons.map(greet);
messages; // => ['Hello, Cristina!', 'Hello, Ana!']
Copier après la connexion

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!']
Copier après la connexion

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'
Copier après la connexion

其中 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
Copier après la connexion

字符串类型的 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'
Copier après la connexion

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)
Copier après la connexion

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!'
Copier après la connexion

DOM 事件侦听器还异步调用事件处理函数(回调函数的子类型):

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

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

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);
}
Copier après la connexion
Copier après la connexion
Et si vous saluez beaucoup de personnes ? Vous pouvez utiliser la méthode de tableau spéciale array.map() pour réaliser : 🎜
const button = document.getElementById('fetchUsersButton');

button.addEventListener('click', fetchUserNames);
Copier après la connexion
Copier après la connexion
🎜persons.map(greet) Obtenir tous les éléments des persons array , et appelez la fonction greet() avec chaque élément comme paramètre d'appel : `greet('Cristina'), greet('Ana'). 🎜🎜Fait intéressant, la méthode <code>persons.map(greet) peut accepter la fonction greet() comme paramètre. De cette façon, greet() devient une 🎜fonction de rappel🎜. 🎜🎜persons.map(greet) est une fonction qui prend une autre fonction comme paramètre, elle est donc appelée une 🎜fonction d'ordre supérieur🎜. 🎜
La fonction de rappel est utilisée comme paramètre de la fonction d'ordre supérieur, et la fonction d'ordre supérieur effectue des opérations en appelant la fonction de rappel.
🎜L'important est que la fonction d'ordre supérieur soit chargée d'appeler le rappel et de lui fournir les paramètres corrects. 🎜🎜Dans l'exemple précédent, la fonction d'ordre supérieur persons.map(greet) est chargée d'appeler la fonction greet() et de convertir tous les éléments du tableau ' Cristina' et Ana ' comme paramètres. 🎜🎜Cela fournit une règle simple pour identifier les rappels. Si vous définissez une fonction et la fournissez comme paramètre à une autre fonction, cela crée un rappel. 🎜🎜Vous pouvez écrire vos propres fonctions d'ordre supérieur à l'aide de rappels. Voici la version équivalente de la méthode array.map() : 🎜rrreee🎜map(array, callback) est une fonction d'ordre supérieur car elle prend une fonction de rappel comme un paramètre, La fonction de rappel est alors appelée à l'intérieur de son corps : callback(item). 🎜🎜Notez que les fonctions régulières (définies avec le mot-clé function) ou les fonctions fléchées (définies avec la flèche épaisse =>) peuvent également être utilisées comme rappels. 🎜🎜🎜2. Rappel synchrone 🎜🎜🎜Il existe deux méthodes de rappel : 🎜synchrone🎜 et 🎜asynchrone🎜callback. 🎜🎜Les rappels synchrones sont "bloquants" : la fonction d'ordre supérieur ne continue pas son exécution tant que la fonction de rappel n'est pas terminée. 🎜🎜Par exemple, appelez les fonctions map() et greet(). 🎜rrreee🎜où greet() est un rappel synchrone. 🎜🎜Étapes pour un rappel synchrone : 🎜
  • 🎜La fonction d'ordre supérieur démarre l'exécution : 'map() démarre'🎜 li >
  • 🎜Exécution de la fonction de rappel : 'greet() appelé'🎜
  • 🎜 Enfin, la fonction d'ordre supérieur termine son propre processus d'exécution : '. map() terminé'🎜

🎜Exemple de rappels synchrones🎜

🎜De nombreuses méthodes de type JavaScript natif utilisent des rappels synchrones. 🎜🎜La méthode la plus couramment utilisée est le tableau, par exemple : array.map(callback), array.forEach(callback), array.find(callback) , <code>array.filter(callback), array.reduce(callback, init)🎜rrreee🎜string.replace(callback) La méthode peut également accepter des rappels exécutés de manière synchrone : 🎜rrreee🎜🎜3. Rappels asynchrones 🎜🎜🎜Les rappels asynchrones sont "non bloquants" : les fonctions d'ordre supérieur peuvent terminer leur exécution sans attendre la fin du rappel. Les fonctions d'ordre supérieur garantissent que les rappels sont exécutés ultérieurement sur des événements spécifiques. 🎜🎜Dans l'exemple suivant, l'exécution de la fonction <code>later() est retardée de 2 secondes : 🎜rrreee🎜later() est un rappel asynchrone car setTimeout (plus tard, 2000) démarre et termine l'exécution, mais later() s'exécute après 2 secondes. 🎜🎜Étapes pour appeler le rappel de manière asynchrone : 🎜
  • 🎜La fonction d'ordre supérieur démarre l'exécution : 'setTimeout()starts'🎜
  • 🎜La fonction d'ordre supérieur termine son exécution : 'setTimeout() terminé'🎜
  • 🎜La fonction de rappel est exécutée après 2 secondes : 'plus tard ( ) appelé'🎜

🎜Exemple de rappel asynchrone🎜

🎜La fonction Timer appelle le rappel de manière asynchrone :🎜rrreee🎜L'écouteur d'événements DOM appelle également le gestionnaire d'événements de manière asynchrone Fonction (sous-type de fonction de rappel) : 🎜rrreee🎜🎜4. Fonction de rappel asynchrone et fonction asynchrone 🎜🎜🎜 L'ajout du mot-clé spécial async avant la définition de la fonction créera une fonction asynchrone : 🎜
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);
}
Copier après la connexion
Copier après la connexion

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);
Copier après la connexion
Copier après la connexion

总结

回调是一个可以作为参数传给另一个函数(高阶函数)执行的函数。

回调函数有两种:同步和异步。

  • 同步回调是阻塞的。

  • 异步回调是非阻塞的。

【相关推荐:javascript学习教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal