The callback function
Xiao Ming in the milk tea shop ordered milk tea, the clerk starts to make milk tea, the “milk tea” and “xiao Ming waiting for milk tea” is a different two events at the same time (task), then, xiao Ming get clerk to make successful milk tea from “milk tea” the event to obtain the result, so xiao Ming was able to complete the events “to buy milk tea”. What if, in the case of “buying milk tea”, Xiao Ming does not want to wait but wants to do something else, such as buying ice cream.
Now, let’s extract this case as an event represented by a JavaScript function:
// Xiao Ming bought milk tea
function buyTea() {
console.log('Buy milk tea... ')}// The shop assistant made milk tea
function makeTea() {
console.log('Making milk tea... ')}// Another incident of Xiao Ming
function buyIcecream() {
console.log('Buying ice cream... ')}Copy the code
Currently, these events are synchronous tasks (events) that are executed from top to bottom by the main thread and cannot execute multiple events at the same time. You will see that these events are independent of each other. How to combine them organically and orderly is a problem.
Therefore, in JavaScript, there is a workaround called asynchronous tasks (events) that combine these independent events using callback functions. In JavaScript, functions exist as first class citizens and can be passed as objects to methods called as arguments, i.e. callback functions.
Go to “[JS] Functions as Values” to see what functions are used as values and can be passed and called as function arguments.
function buyThing(money = 0, callback) {
let isPay = false
if (money >= 5) isPay = true
callback(isPay)
}
buyThing(10.function (isPay) {
if (isPay) {
console.log('Buy milk tea... ')
setTimeout(() = > {
console.log('Milk tea made!! ')
buyThing(20.function (isPay) {
if (isPay) {
console.log('Buying ice cream... ')
setTimeout(() = > {
console.log('Ice cream made!! ')
buyThing(30.function(isPay) {
/ /... Do a lot of events and call the buyThing function multiple times})},2000)}else {
console.log('Outstanding amount... ')}})},1000)}else {
console.log('Outstanding amount... ')}})Copy the code
The callback function does combine these separate events organically, but then comes callback hell.
Now we understand the benefits of the callback function. Here’s another example to get a better idea of the benefits of the callback function. If you implement a simple calculator with functions such as addition, subtraction, multiplication, and division, you usually think of a function that takes two arguments and passes the operation types as strings to fulfill different requirements.
function calculate(x, y, type) {
if (type == 'minus') {
return x - y
} else if (type == 'add') {
return x + y
} ......
}
let result = calculate(10.20.'minus') / / - 10
Copy the code
There is an obvious problem with the above code. If you make additional restrictions in subtraction (or add functionality to the source code), it affects the entire Calculate function itself. Again, if I extend the calculate function, it affects the function itself. In this case, we look to the callback function to solve the problem.
// Make some basic judgments about the calculate ontology (limitations)
function calculate(x, y, callback) {
if (x < 0 || y < 0) {
throw new Error(`Numbers must not be negative! `)}if (typeofx ! = ='number' || typeofy ! = ='number') {
throw new Error(`Args must be number type! `)}return callback(x, y, 'not problem!!! ') // Provide more details
}
// There are no additional restrictions
calculate(10.20.function (x, y, more) {
console.log(more) // 'not problem!!! '
return x * y
})
// Make some additional restrictions
calculate(5.5.function (x, y, more) {
console.log(more) // 'not problem!!! '
if (x + y <= 10) {
throw new Error(
'The sum of the two numbers must be greater than 10')}return x * y
})
Copy the code
Now, when you call the Calculate function, you can place additional restrictions in the callback that do not affect the calculate function itself. Also, the callback function gets more detail from the Calculate function body, which allows us to do more with it.
let points = [40.100 ,10.5.25]
points.sort(function (a, b) = >{
return a - b
}) // [5, 10, 25, 40, 100]
/ /... Another way of comparing...
points.sort(function (a, b) = >{
if (a < b) {
return -1
}
if (a > b) {
return 1
}
return 0
}) // [5, 10, 25, 40, 100]
Copy the code
The different sorts in the callback function can determine the final result. The callback function makes the program more flexible.
The callback hell
In the case of “Xiao Ming bought milk tea” above, the callback is nested inside the callback, and the code shape looks like a pyramid rotated 180°. This nesting is called callback hell.
Thus, Promise solves the problem of callback hell. A Promise is an object that represents the final completion (or failure) of an asynchronous operation and its resulting value.
Use Promise to solve “Xiaoming buy milk tea” callback hell:
function buyThing(money, timeout) {
const promise = new Promise((resolve, reject) = > {
console.log('The event is ongoing... ')
setTimeout(() = > {
if (money >= 5) {
console.log('Payment Amount:${money}`)
resolve('success to pay! ')}else {
reject('unsuccess to pay! ')
}
}, timeout)
})
return promise
}
buyThing(10.1000)
.then((res) = > {
console.log('Milk tea made!! ')
return buyThing(20.2000)
})
.then((res) = > {
console.log('Ice cream made!! ')})Copy the code
At the code level, the “pyramid” phenomenon caused by multiple callback function calls is resolved with the use of Promise. Let’s look at the implementation:
Please turn to MDN explanation about Promise: Promise – JavaScript | MDN