Maison >interface Web >js tutoriel >Comprendre les rappels dans JS

Comprendre les rappels dans JS

青灯夜游
青灯夜游avant
2020-10-21 17:47:322494parcourir

Comprendre les rappels dans JS

Avez-vous déjà vu « rappel » accidentellement sans savoir ce que cela signifiait ? Ne t'inquiète pas. Vous n'êtes pas seul. De nombreux débutants en JavaScript ont du mal à comprendre les rappels.

Bien que les rappels puissent prêter à confusion, vous devez quand même apprendre à les comprendre parfaitement car il s'agit d'un concept essentiel en JavaScript. Si vous ne connaissez pas les reculs, vous ne pouvez pas aller loin.

C’est ce que l’article d’aujourd’hui doit expliquer ! Vous allez apprendre ce que sont les rappels, pourquoi ils sont importants et comment les utiliser.

Dans cet article, vous verrez les fonctions fléchées dans ES6. Si vous ne les connaissez pas encore, je vous suggère de consulter d'abord le post ES6. (Lisez simplement la section sur les fonctions des flèches).

Qu'est-ce qu'un rappel ?

Un rappel est une fonction qui est passée en paramètre à une autre fonction et exécutée ultérieurement. (Les développeurs disent qu'une autre fonction est appelée lorsqu'une fonction est exécutée, c'est pourquoi callbacks est appelé un rappel).

Ils sont si courants en JavaScript que vous les avez peut-être utilisés sans savoir qu'il s'agit de fonctions de rappel.

Un exemple qui peut recevoir une fonction de rappel est addEventLisnter :

const button = document.querySelector('button')
button.addEventListener('click', function(e) {
    // Adds clicked class to button
    this.classList.add('clicked')
})

N'avez-vous pas vu qu'il s'agit d'un rappel ? Jetons un coup d'œil à l'exemple suivant.

const button = document.querySelector('button')

// Function that adds 'clicked' class to the element
function clicked (e) {
    this.classList.add('clicked')
}

// Adds click function as a callback to the event listener
button.addEventListener('click', clicked)

Ici, nous lions l'événement click à un bouton via JavaScript. Une fois l'heure du clic détectée, JavaScript exécutera la fonction clicked. Ainsi, dans cet exemple, lorsque la fonction addEventListener reçoit une fonction de rappel, clicked est un rappel.

Savez-vous ce qu'est un rappel maintenant ? :)

Regardons un autre exemple. Cette fois, supposons que vous souhaitiez filtrer un tableau de nombres pour obtenir une liste de ceux inférieurs à 5. Ici, vous transmettez une fonction de rappel à la fonction filter.

const numbers = [3, 4, 10, 20]
const lesserThanFive = numbers.filter(num => num < 5)

Maintenant, si vous modifiez le code ci-dessus pour utiliser une fonction nommée, le tableau de filtres ressemblera à ceci :

const numbers = [3, 4, 10, 20]
const getLessThanFive = num => num < 5

// Passing getLessThanFive function into filter
const lesserThanFive = numbers.filter(getLessThanFive)

Dans cet exemple, getLessThanFive est un rappel. Array.filter est une fonction qui peut recevoir des rappels.

Regardez maintenant ? Lorsque vous connaissez les rappels, vous les trouverez partout.

L'exemple suivant vous montre comment écrire une fonction de rappel et une fonction pouvant recevoir des rappels.

// Create a function that accepts another function as an argument
const callbackAcceptingFunction = (fn) => {
    // Calls the function with any required arguments
    return fn(1, 2, 3)
}

// Callback gets arguments from the above call
const callback = (arg1, arg2, arg3) => {
    return arg1 + arg2 + arg3
}

// Passing a callback into a callback accepting function
const result = callbackAcceptingFunction(callback)
console.log(result) // 6

Veuillez noter que lorsque vous transmettez le rappel à une autre fonction, vous transmettez simplement la référence (non exécutée, donc il n'y a pas de ())

`const result = callbackAcceptingFunction(callback)`

Vous ne pouvez la transmettre que dans callbackAcceptingFunction Appelez ce rappel dans Pass inside :

const callbackAcceptingFunction = (fn) => {
    // Calls the callback with three args
    fn(1, 2, 3)
}

Il s'agit d'une structure de rappel. Maintenant, vous savez que callbackAcceptingFunction contient le paramètre

:

// Callback gets arguments from callbackAcceptingFunction
const callback = (arg1, arg2, arg3) => {
    return arg1 + arg2 + arg3
}
addEventListener Ouf ! C’est la signification fondamentale du rappel ! N'oubliez pas le mot-clé : passez une fonction dans une autre et vous rappellerez le mécanisme mentionné ci-dessus. event

Cette capacité à transférer des fonctions est un gros problème. C'est tellement gros que les fonctions en JavaScript sont des fonctions d'ordre supérieur. Les fonctions d'ordre supérieur sont très importantes dans le paradigme de programmation fonctionnelle.

Mais nous n’abordons pas ce sujet maintenant. Maintenant, je suis sûr que vous connaissez déjà les rappels et comment les utiliser. Mais pourquoi avez-vous besoin d’utiliser des rappels ?


Pourquoi utiliser les rappels ?

Les rappels peuvent être utilisés de deux manières différentes : dans les fonctions synchrones et dans les fonctions asynchrones.

Rappels dans les fonctions synchronisées

Si votre exécution de code se fait de haut en bas, de faire à droite, de manière séquentielle, il attendra que le l'exécution du code est terminée avant l'exécution de la ligne de code suivante, votre code est alors synchrone.

Regardons un exemple pour une compréhension plus facile :

// Now you know where this event object comes from! :)
button.addEventListener(&#39;click&#39;, (event) => {
    event.preventDefault()
})

Dans l'exemple ci-dessus,

est exécuté en premier. Une fois l'exécution terminée,

commence l'exécution. Lorsque

l'exécution est terminée, addOne(1) démarre l'exécution. Ce processus se poursuit jusqu'à ce que la dernière ligne de code soit exécutée. addOne(2)addOne(2)Mais lorsque vous souhaitez faciliter l'échange d'une partie du code avec d'autres, vous pouvez utiliser des rappels dans les fonctions synchrones. addOne(3)

Donc, revenons à l'exemple

ci-dessus, tout en filtrant le tableau pour contenir des nombres inférieurs à

, vous pouvez également réutiliser

pour contenir des nombres supérieurs à Array.filter. 5

const addOne = (n) => n + 1
addOne(1) // 2
addOne(2) // 3
addOne(3) // 4
addOne(4) // 5
Array.filterC'est pourquoi vous utilisez des rappels dans les fonctions synchrones. Maintenant, passons à autre chose et voyons pourquoi nous utilisons les rappels dans les fonctions asynchrones. 10

Rappel en fonction asynchrone

这里异步的意思是,如果 JavaScript 需要等待某个东西完成,在等待的过程中会执行其余的任务。

一个异步函数例子就是setTimeout。它会一段时间后执行回调函数。

// Calls the callback after 1 second
setTimeout(callback, 1000)

如果你给JavaScript 另一个任务去完成时我们看看setTimeout是怎么工作的:

const tenSecondsLater = _ = > console.log(&#39;10 seconds passed!&#39;)

setTimeout(tenSecondsLater, 10000)
console.log(&#39;Start!&#39;)

在上面的代码里,JavaScript 去执行setTimeout。这时,会等待10秒且打印日志“10 seconds passed!”。

同时,在等到10秒去执行setTimeout时,JavaScript 会执行console.log("Start!")

因此,如果你记录上面的代码,你会看到这一点。

// What happens:
// > Start! (almost immediately)
// > 10 seconds passed! (after ten seconds)

啊。异步操作听起来很复杂,不是么?但是我们为什么在 JavaScript 里到处使用呢?

要理解为什么异步操作很重要,想象一下 JavaScript 是你家里的一个机器人助手。这个助手很蠢。一次只能做一件事情。(这个行为称之为单线程)。

假设你告诉机器人助手帮你订点披萨。但是机器人助手如此蠢,在给披萨店打完电话后,机器人助手坐在你家门前,慢慢的等待披萨送来。在这个过程中不能做任何其他的事情。

等待的过程中,你不能让它去熨烫衣服,拖地板以及其他任何事情。你需要等20分钟,直到披萨送来,才愿意做其他的事情。

这个行为称之为阻塞。在等待一个任务执行完全之前,其他的操作被阻止了。

const orderPizza = flavour => {
    callPizzaShop(`I want a ${flavour} pizza`)
    waits20minsForPizzaToCome() // Nothing else can happen here
    bringPizzaToYou()
}

orderPizza(&#39;Hawaiian&#39;)

// These two only starts after orderPizza is completed
mopFloor()
ironClothes()

现在,阻塞操作是非常令人失望的。

为什么?

我们把愚蠢的机器人助手放在浏览器的运行环境里。想象一下,当按钮被点击时需要改变按钮的颜色。

那这个愚蠢的机器人会怎么做呢?

它会凝视着这个按钮,在按钮被点击之前,忽略掉其他任何的命令。同时,用户不能选择其他任何东西。看看现在这样的情况?这就是异步编程在 JavaScript 为什么如此重要。

但是真正理解在异步操作过程中发生了什么,我们需要理解另外一个东西-事件循环。

事件循环

想象事件循环,可以想象 JavaScript 是一个 todo-list 的管家。这个列表包含了所有你告诉它的事情。JavaScript 会按照你给的顺序,一步步的遍历这个列表。

假设你给JavaScript 的5个命令如下:

const addOne = (n) => n + 1

addOne(1) // 2
addOne(2) // 3
addOne(3) // 4
addOne(4) // 5
addOne(5) // 6

这将会出现在 JavaScript 的todo 列表里。

Comprendre les rappels dans JS

命令在 JavaScript 的 todo 列表里同步显示。

除了 todo 列表,JavaScript 还保存了一个 waiting 列表,这个列表可以跟踪需要等待的东西。如果你告诉 JavaScript 需要定披萨,它会给披萨店打电话,并把"等待披萨送来"加到等到列表里。同时,它会做 todo 列表已经有的事情。

所以,想象一下有这样的代码。

const orderPizza (flavor, callback) {
    callPizzaShop(`I want a ${flavor} pizza`)
    // Note: these three lines is pseudo code, not actual JavaScript
    whenPizzaComesBack {
        callback()
    }
}

const layTheTable = _ => console.log(&#39;laying the table&#39;)

orderPizza(&#39;Hawaiian&#39;, layTheTable)
mopFloor()
ironClothes()

JavaScript 的初始列表将会是:

Comprendre les rappels dans JS

定披萨,拖地和熨烫衣服!

这是,当执行到orderPizza,JavaScript 知道需要等待披萨送来。因此,在把"等待披萨送来"加到等待列表中的同时会处理剩下的工作。

Comprendre les rappels dans JS

JavaScript 等待披萨到达。

当披萨送到时,按门铃会通知 JavaScript并做一个标记,当处理完其他杂事时,会去执行layTheTable

Comprendre les rappels dans JS

JavaScript 知道通过标记里的命令需要去执行layTheTable

然后,一旦处理完了其他的杂务,JavaScript 就会执行回调函数layTheTable

Comprendre les rappels dans JS

当其他一切都完成时, JavaScript 会将其放置。

这就是我的朋友,事件循环。你可以用事件循环中的实际关键字来替代我们的巴特勒类比来理解所有的事情。

  • Todo-list-> Call stack

  • Waiting-list-> Web apis

  • Mental note-> Event queue

Comprendre les rappels dans JS

JavaScript 事件循环

如果你有20分钟空闲时间的话,我强烈推荐你看Philip Roberts在 JSConf 上关于事件循环的演讲。它会帮助你了解事件循环里的细节。

为什么回调如此重要?

哦。我们在事件循环上转了个大圈。现在我们回头来看。

之前,我们提到如果 JavaScript 专注地盯着一个按钮并忽略其他所有的命令,这是非常糟糕的。是吧?

通过异步回调,我们可以提前给 JavaScript 指令而不需要停止整个操作。

现在,当你让 JavaScript 监听一个按钮的点击事件时,它将"监听按钮"放在等待列表里,然后继续做家务。当按钮最终获取到点击事件时,JavaScript 会激活回调,然后继续运行

下面是一些常见的回调函数,告诉 JavaScript 应该怎么做:

  • 当事件被触发(比如:addEventListener

  • Ajax 执行之后(比如:jQuery.ajax

  • 文件读写之后(比如:fs.readFile)

// Callbacks in event listeners
document.addEventListener(button, highlightTheButton)
document.removeEventListener(button, highlightTheButton)

// Callbacks in jQuery&#39;s ajax method
$.ajax(&#39;some-url&#39;, {
    success (data) { /* success callback */ },
    error (err) { /* error callback */}
});

// Callbacks in Node
fs.readFile(&#39;pathToDirectory&#39;, (err, data) => {
    if (err) throw err
    console.log(data)
})

// Callbacks in ExpressJS
app.get(&#39;/&#39;, (req, res) => res.sendFile(index.html))

这就是回调!

希望,你现在已经弄清楚了回调是什么并且怎么去使用。在最开始的时候,你没必要创建很多的回调,更多的去专注于学习如何使用可用的回调函数。

现在,在结束之前,我们来看看回调的第一个问题 - 回调地狱

回调地狱

回调地狱是在多个回调嵌套出现时的一个现象。它发生在一个异步回调执行依赖上一个异步回调执行的时候。这些嵌套的回调会导致代码非常难以理解。

在我的经验里,你只会在 Node.js 里看到回调地狱。当你的 JavaScript 在前台运行时一般都不会遇到回调地狱。

这里有一个回调地狱的例子:

// Look at three layers of callback in this code!
app.get(&#39;/&#39;, function (req, res) {
    Users.findOne({ _id:req.body.id }, function (err, user) {
        if (user) {
            user.update({/* params to update */}, function (err, document) {
            res.json({user: document})
        })
        } else {
            user.create(req.body, function(err, document) {
                res.json({user: document})
            })
        }
     })
})

现在,对你来说,解读上面的代码是一个挑战。相当的难,不是么?难怪在看到嵌套回调时,开发人员会不寒而栗。

解决回调的一个解决方案是将回调函数分解成更小的部分,以减少嵌套代码的数量

const updateUser = (req, res) => {
    user.update({/* params to update */}, function () {
        if (err) throw err;
        return res.json(user)
    })
}

const createUser = (req, res, err, user) => {
    user.create(req.body, function(err, user) {
        res.json(user)
    })
}

app.get(&#39;/&#39;, function (req, res) {
    Users.findOne({ _id:req.body.id }, (err, user) => {
        if (err) throw err
        if (user) {
            updateUser(req, res)
        } else {
            createUser(req, res)
        }
    })
})

阅读起来容易得多,不是么?

在新的JavaScript 版本里,还有一些新的解决回调地狱的方法,比如: promisesasync/await。但是,会在另一个话题中解析它们。

结语

今天,我们学习了什么是回调,为什么会如此重要以及如何去使用它们。同时学习到了什么是回调地狱,以及如何避免。希望,回调不会让你感到害怕。

关于回调你还有其他的问题么?如果你有的话,请在下面留言,我会尽快回复你的。

相关免费学习推荐:js视频教程

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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer