这篇难度有点大,知识点有点多,所以先补习一下callback:
作为一个前端,竟然很少写js代码,有时候我都怀疑我是不是应该换家公司,从头做起。可惜当年的我,没有什么胆量(现在的我依旧很怂😭 )。俗话都说,出来混的都是要还的 ;以前欠的,如今也是要还的。今天就开始从callback 补起来。
回调函数
A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.
被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。
说人话就是,将一个函数以参数的形式传入到另一个函数。
以下是mdn举出的例子,一度让我怀疑我的智商,这么简单,我怎么会不懂!!!!
function greeting(name) {
alert('Hello ' + name);
}
function processUserInput(callback) {
var name = prompt('Please enter your name.');
callback(name);
}
processUserInput(greeting);
看起来很简单是吧。
其实如果你查看w3schools, www.w3schools.com/js/js_callb….在结尾的时候,他们留了一段话,
The examples above are not very exciting. They are simplified to teach you the callback syntax. Where callbacks really shine are in asynchronous functions, where one function has to wait for another function (like waiting for a file to load).
大致翻译过来就是,你以为回调函数就是这样吗!!!这里只是强行降智,让你先理解一下,后面 有你受的。。。。
此时的我(某个时间点看一些大佬代码的我),
回归正传,以下是我看另一个教程的笔记,原址会贴在文章尾部,
如果某天,突然飘出以下代码,并且让你进行一些修改,你会怎么做呢?
function filter(numbers) {
let results = [];
for (const number of numbers) {
if (number % 2 != 0) {
results.push(number);
}
}
return results;
}
let numbers = [1, 2, 4, 7, 3, 5, 6];
console.log(filter(numbers));
相信大多数人能看懂,不解释。。。
直接贴上修改要求:要求这个filter函数既能筛选出偶数,又能筛选出奇数。
你会怎么改呢??我的错误答案就不贴在这啦,以免误导他人,直接上原答案,
function isOdd(number) {
return number % 2 != 0;
}
function isEven(number) {
return number % 2 == 0;
}
function filter(numbers, fn) {
let results = [];
for (const number of numbers) {
if (fn(number)) {
results.push(number);
}
}
return results;
}
let numbers = [1, 2, 4, 7, 3, 5, 6];
console.log(filter(numbers, isOdd));
console.log(filter(numbers, isEven));
(说不出哪里好,但是就是感觉代码很厉害👍)
By definition, the
isOddandisEvenare callback functions or callbacks. Because thefilter()function accepts a function as an argument, it’s called a high-order function..
其中,isOdd 和 isEven 就是回调函数。本来好好的函数,却被硬当成参数,传入函数filter()中。(为什么有种逼良为娼的既视感?) filter()函数就是高阶函数。
回调函数也可以是匿名函数,所以进阶版的代码第一版:
进击的函数 :
function filter(numbers, callback) {
let results = [];
for (const number of numbers) {
if (callback(number)) {
results.push(number);
}
}
return results;
}
let numbers = [1, 2, 4, 7, 3, 5, 6];
let oddNumbers = filter(numbers, function (number) {
return number % 2 != 0;
});
console.log(oddNumbers);
如果你的脑容量够大的话,这里还有进击的函数升华版 es6,
function filter(numbers, callback) {
let results = [];
for (const number of numbers) {
if (callback(number)) {
results.push(number);
}
}
return results;
}
let numbers = [1, 2, 4, 7, 3, 5, 6];
let oddNumbers = filter(numbers, (number) => number % 2 != 0);
console.log(oddNumbers);
同步回调 synchronous callbacks
A synchronous callback is executed during the execution of the high-order function that uses the callback. The isOdd and isEven are examples of synchronous callbacks because they execute during the execution of the filter() function.
同步回调是指在高阶函数执行过程中被调用并且执行的过程。在文中回调函数,isOdd和 isEven就是同步回调,因为他们在高阶函数filter() 执行过程中被调用并且执行。(原谅本人翻译能力有限,不是很严谨)
异步回调 asynchronous callbacks
An asynchronous callback is executed after the execution of the high-order function that uses the callback.
异步回调是指高阶函数执行后,再执行的回调函数。注意顺序哦,高阶函数执行后,再执行。
Asynchronicity means that if JavaScript has to wait for an operation to complete, it will execute the rest of the code while waiting.
异步的意思是如果JavaScript必须要等另一个程序执行完毕,JavaScript就会在等待的过程中执行剩下的代码。
假设你需要写一段代码,从远程服务器下载一张图片,并且在下载完成后对图片进行简单的加工或处理。
function download(url) {
// 为了模拟真实网速,通常用setTimeout来代替
setTimeout(() => {
// script to download the picture here
console.log(`Downloading ${url} ...`);
},1000);
}
function process(picture) {
console.log(`Processing ${picture}`);
}
let url = '<https://www.javascripttutorial.net/pic.jpg>';
download(url);
process(picture);
执行代码之后,console显示
Processing <https://javascripttutorial.net/pic.jpg>
Downloading <https://javascripttutorial.net/pic.jpg> ...
为什么不是downloading 先执行呢??
因为setTimeout 是javascript中本身就是的异步函数,自带异步光环。所以process先被执行。。。 javascript 之中,处处黑魔法。。。
回归正题,正确的执行顺序应该是先下载,在处理图片。为了解决此问题,我们就要用到回调函数。在异步函数当中调用另一个函数,而不是绕过异步函数。(我也不知道我写的什么鬼)上代码。。。
function download(url, callback) {
setTimeout(() => {
// script to download the picture here
console.log(`Downloading ${url} ...`);
// process the picture once it is completed
callback(url);
}, 1000);
}
function process(picture) {
console.log(`Processing ${picture}`);
}
let url = '<https://wwww.javascripttutorial.net/pic.jpg>';
download(url, process);
console 输出:
Downloading <https://www.javascripttutorial.net/pic.jpg> ...
Processing <https://www.javascripttutorial.net/pic.jpg>
上面代码中,process函数就是回调函数。它被当作参数传入到函数download, 并且在异步函数setTimeout内部中调用,感觉可以理解为打入敌人内部。
When you use a callback to continue code execution after an asynchronous operation, the callback is called an asynchronous callback.
当你利用回调函数这个¨黑魔法¨来迫使其在异步操作之后执行,这个就叫做异步回调。(翻译无能,自跪键盘3秒钟 )
回调地狱
简单一笔带过,回调地狱部分,
想象一下,你如果需要依次下载三张图片,你该怎么调用上面的代码,像这样?
function download(url, callback) {
setTimeout(() => {
console.log(`Downloading ${url} ...`);
callback(url);
}, 1000);
}
const url1 = '<https://www.javascripttutorial.net/pic1.jpg>';
const url2 = '<https://www.javascripttutorial.net/pic2.jpg>';
const url3 = '<https://www.javascripttutorial.net/pic3.jpg>';
download(url1, function (url) {
console.log(`Processing ${url}`);
download(url2, function (url) {
console.log(`Processing ${url}`);
download(url3, function (url) {
console.log(`Processing ${url}`);
});
});
});
恭喜你,你成功的创建了一个地狱给下一个维护代码的码农。
以下为原文:
Nesting many asynchronous functions inside callbacks is known as the pyramid of doom or the callback hell:
asyncFunction(function(){
asyncFunction(function(){
asyncFunction(function(){
asyncFunction(function(){
asyncFunction(function(){
....
});
});
});
});
});
Code language: JavaScript (javascript)
To avoid the pyramid of doom, you use promises or async/await functions.
小声BB, 其实我觉的代码看起来挺好看的,希望路过的同行不要打我。
回调函数的学习资料