[js]回调函数和回调地狱(代码片段)

shiramashiro shiramashiro     2022-12-04     676

关键词:

回调函数

小明在奶茶店点了奶茶,店员开始制作奶茶,此时“制作奶茶”与“小明等待奶茶”是一个同时进行的不同的两个事件(任务),那么,小明获取店员制作成功的奶茶是从“制作奶茶”这一事件获取的结果,所以小明才能够完成“购买奶茶”这一事件。如果,小明在“购买奶茶”这一事件中,不想一直等待而是想去做一些其他的事情,比如购买冰淇淋。

现在,我们将这一案例抽取为一个个事件,用 JavaScript 函数体现出来:

// 小明购买奶茶事件
function buyTea() 
    console.log(\'购买奶茶...\')


// 店员制作奶茶事件
function makeTea() 
    console.log(\'制作奶茶...\')


// 小明的另一个事件
function buyIcecream() 
    console.log(\'购买冰淇淋...\')

目前,这些事件属于同步任务(事件),它们被主线程由上到下依次执行,无法在同一时间内执行多个事件。你会发现,这些事件之间是各自独立的。如何将它们有机地、有序地结合在一起是一个问题。

因此,在 JavaScript 中,有一种解决方式叫做异步任务(事件),利用回调函数将这些独立的事件结合在一起。在 JavaScript 中,函数作为第一等公民的存在,可以将函数作为对象传递给方法作为实参进行调用,即回调函数

请转至“[JS]函数作为值”一文,了解什么函数是如何作为值,并且可以作为函数实参进行传递以及调用的。

function buyThing(money = 0, callback) 
    let isPay = false
    if (money >= 5) isPay = true
    callback(isPay)


buyThing(10, function (isPay) 
    if (isPay) 
        console.log(\'购买奶茶...\')
        setTimeout(() => 
            console.log(\'奶茶制作完成!!!\')
            buyThing(20, function (isPay) 
                if (isPay) 
                    console.log(\'购买冰淇淋...\')
                    setTimeout(() => 
                        console.log(\'冰淇淋制作完成!!!\')
                        buyThing(30, function(isPay) 
                            // ...做很多事件,多次调用buyThing函数
                        )
                    , 2000)
                 else 
                    console.log(\'未支付金额...\')
                
            )
        , 1000)
     else 
        console.log(\'未支付金额...\')
    
)

回调函数的确将这些独立事件有机地结合在一起了,但是随之而来的就是回调地狱。

现在,我们明白了回调函数的好处。再列举一个例子,深入了解回调函数的好处在哪。若实现一个简单的计算器,其功能有加、减、乘、除等运算,通常情况下会想到一个函数获得两个参数,并将运算类型作为字符串传递给函数参数以实现不同需求。

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

上述代码,存在一个明显的问题。如果在减法中做其他的限制条件(或增加源代码的功能),它会影响到整个 calculate 函数本身。再者,如果我扩展 calculate 函数功能,它也会影响到函数本身。对于这种情况,我们寄希望于回调函数,通过它来解决这个问题。

// calculate本体做一些基本的判断(限制)
function calculate(x, y, callback) 
    if (x < 0 || y < 0) 
        throw new Error(`Numbers must not be negative!`)
    
    if (typeof x !== \'number\' || typeof y !== \'number\') 
        throw new Error(`Args must be number type!`)
    
    return callback(x, y, \'not problem!!!\') // 向外提供更多细节


// 没有做任何附加限制
calculate(10, 20, function (x, y, more) 
    console.log(more) // \'not problem!!!\'
    return x * y
)

// 做了一些附加限制
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
)

现在,调用 calculate 函数时,可以在回调函数中做一些附加限制条件,它不会影响到 calculate 这个函数本体。并且,回调函数可以从 calculate 函数本体中获取更多的细节(信息),通过这些信息我们又能做出更多的操作。

let points = [40, 100 ,10, 5, 25]

points.sort(function (a, b) => 
    return a - b
) // [5, 10, 25, 40, 100]

// ...另一种比较方式...
points.sort(function (a, b) => 
    if (a < b) 
        return -1
    
    if (a > b) 
        return 1
    
    return 0
) // [5, 10, 25, 40, 100]

在回调函数中不同的排序方式可以决定最后的结果。回调函数使得程序更加灵活

回调地狱

在上面的“小明买奶茶”案例中,回调内部再嵌套回调,其代码形状上看着像180°旋转之后的金字塔,这种层层嵌套就是回调地狱。

因此,Promise 可以解决回调地狱的问题。Promise 是一个对象,用于表示一个异步操作的最终完成(或失败)及其结果值。

利用 Promise 解决“小明买奶茶”回调地狱:

function buyThing(money, timeout) 
    const promise = new Promise((resolve, reject) => 
        console.log(\'事件正在进行中...\')
        setTimeout(() => 
            if (money >= 5) 
                console.log(`支付金额:$money`)
                resolve(\'success to pay!\')
             else 
                reject(\'unsuccess to pay!\')
            
        , timeout)
    )
    return promise


buyThing(10, 1000)
    .then((res) => 
        console.log(\'奶茶制作完成!!!\')
        return buyThing(20, 2000)
    )
    .then((res) => 
        console.log(\'冰淇淋制作完成!!!\')
    )

在代码层面,使用 Promise 之后,解决了多层回调函数调用导致的“金字塔”现象。让我们看看实现效果:

请转至 MDN 关于 Promise 的解释:Promise - JavaScript | MDN

什么是地狱回调?解决回调地狱的两种方法(代码片段)

地狱回调概念:回调函数套回调函数的情况就叫做回调地狱,//地狱回调setTimeout(function()//第一层console.log('武林要以和为贵');setTimeout(function()//第二程console.log('要讲武德');setTimeout(function()//第三层console.log(' 查看详情

promise-基础(代码片段)

目录1、回调地狱1.1、概念1.2、回调地狱的缺点1.3、代码2、解决回调地狱2.1、Promise的基本概念2.1.1、Promise是一个构造函数2.1.2、Promise.prototype上包含一个.then()方法2.1.3、.then()方法用来预先指定成功和失败的回调函数2.2、.then()方法... 查看详情

回调地狱和promise(代码片段)

目录1.回调地狱callback-hell由于fs.readFile是异步操作,所以你不能判断下面三个文件的执行顺序varfs=require('fs')fs.readFile('./data/a.txt','utf-8',function(err,data) if(err) throwerr console.log( 查看详情

promise,async/await解决回调地狱(代码片段)

先说一下async的用法,它作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async就是异步的意思,异步函数也就意味着该函数的执行不会阻塞后面代码的执行。写一个async函数asyncfunctiontimeout()  return‘helloworld... 查看详情

javascript承诺回调-地狱(代码片段)

查看详情

异步解决方案的发展历程(代码片段)

1.回调函数(callback)setTimeout(()=>//callback函数体,1000)缺点:回调地狱,不能用trycatch捕获错误,不能return回调地狱的根本问题在于:缺乏顺序性:回调地狱导致的调试困难,和大脑的思维方式不符;嵌套函数存在耦合性,一旦... 查看详情

javascript典型的回调-地狱(代码片段)

查看详情

promise异步(代码片段)

ES5中的回调地狱了解吗?如a回调b,b回调c,c回调d……层层回调,就叫回调地狱//异步,回调函数//1functionloadScript(src,callback)letscript=document.createElement(‘script‘);script.src=src;script.onload=()=>callback();document 查看详情

使用es6的promise完美解决回调地狱(代码片段)

...性能和用户体验。正常的前端会把接口写在另一个接口的回调里。是这样不错,但是它增加了函数的嵌套深度也会造成一定的逻辑混乱。也许有朋友会说,哪那么多毛病,解决问题不就好了吗?但是,如果需要的是另外好几个接... 查看详情

vue.js第三章(代码片段)

promise它将我们从回调地狱中解脱出来创建和使用varfs=require(‘fs‘)//创建promise//reslove表示执行成功后调用的回调函数//reject表示出现错误后调用的回调函数varp1=newPromise((reslove,reject)=>fs.readFile(‘a.txt‘,"utf-8",(err,data)=>if(err)rejec... 查看详情

Node.JS + Mongoose 回调地狱

】Node.JS+Mongoose回调地狱【英文标题】:Node.JS+Mongoosecallbackhell【发布时间】:2017-04-0613:23:01【问题描述】:如何将mongoose保存到db但先等待其他集合加载?平台和流派是空的,因为“保存”功能在平台和流派加载之前运行,请帮助... 查看详情

设计模式之回调机制(代码片段)

#回调模式:把函数作为参数,传递给另一个函数,延迟到另一个函数的某个时刻执行的过程叫回调。#缺点:回调地狱#面向过程的实现方式defcallback(*args,**kwargs):"""回调函数"""#TODO函数体的实现passdefotherFunc(func.*args,**kwargs):"""高阶... 查看详情

promise全解析(代码片段)

...通过ajax请求id,再根据id请求用户名,再根据用户名获取email回调地狱在回调函数中嵌套回调Promise解决了回调地狱2Promise的基本使用Promise是一个构造函数,通过new关键字实例化对象语法newPromise((resolve,reject)=>)Promise接受一个函数... 查看详情

javascript承诺中的无范围回调-地狱修复(代码片段)

查看详情

回调地狱问题(代码片段)

举一个文件操作的例子,先创建3个txt文件,a.txt,b.txt.c.txt 内容分别为 aaaa,bbbbb,cccc`varfs=require(‘fs‘)//文件操作是异步的执行顺序不一定是按顺序执行fs.readFile(‘./data/a.txt‘,‘utf8‘,function(err,data)if(err)//returnconsole.log(‘读取... 查看详情

Node.js 中的回调地狱

】Node.js中的回调地狱【英文标题】:CallbackhellinNode.js【发布时间】:2017-01-0120:13:28【问题描述】:我在Node.js中遇到了“回调地狱”的情况。基本上我想要的是:从静态json文件中读取数据(本地)-->查询MongoDB以从两个单独的... 查看详情

详解回调函数——以js为例解读异步回调和eventloop(代码片段)

 回调,是非常基本的概念,尤其在现今NodeJS诞生与蓬勃发展中变得更加被人们重视。很多朋友学NodeJS,学很久一直摸不着门道,觉得最后在用Express写Web程序,有这样的感觉只能说明没有学懂NodeJS,本质上说不理解回调,就... 查看详情

解决回调地狱

 什么是回调?与大多数运行后立刻给出结果的函数不同,使用回调的函数要花一些时间才能得出结果。难点:理解程序的运行顺序特点:1.回调函数只是储存了将要运行的东西2.不要从上到下阅读顺序什么是回调地狱?fs.readd... 查看详情