【问题标题】:Callback without effect, function still executed before callback回调无效,回调前函数仍执行
【发布时间】:2019-10-23 10:58:26
【问题描述】:

我有一个带有回调的函数,我的问题是函数的返回发生在回调广告之前,然后返回 null 而不是返回坐标数组

function callback(coordinates=[]){
    console.log(coordinates);
    return coordinates;
}
 function getCoordinates(callback){
    connection.connect();
    let coordinates=[null];
    connection.query('SELECT AreaId AS areaNumber, longitude AS longitude, latitude AS latitude FROM coordinates', function (error, results, fields) {
        if (error) throw error;
        let area=[];
        for (result of results) {
            if (!area.includes(result.areaNumber)){
                area.push(result.areaNumber)
                coordinates[result.areaNumber]=[];
            }
            coordinate=[result.longitude,result.latitude];
            coordinates[result.areaNumber].push(coordinate);
        }    
        coordinates=callback(coordinates)
    });
    connection.end();
    return coordinates;
}
console.log(getCoordinates(callback));

我有:

[null] //对应console.log(getCoordinates(callback));

[array with value] //对应于函数vallback中的console.log(coordinates)

我的回调会被考虑怎么办?
抱歉,我是 node.js 的新手,所以我可能误解了回调。 我想要的是获得一个坐标数组,以后可以在我的代码中的 2 个不同位置使用,例如:

.get('/map', function(req, res) {
    let Coords=getCoordinate(callback)
    res.render('map.ejs', {token: tokenMapbox, coordinates: Coords});
})
.get('/wmap', function(req, res) {
    let Coords=getCoordinate(callback)
    res.render('wmap.ejs', {token: tokenMapbox, coordinates: Coords});
})

【问题讨论】:

  • 忘记返回值。需要使用coordinates 的代码应该位于回调中。这可能会有所帮助:felix-kling.de/blog/2019/…,尤其是“误解 1:从回调中返回”
  • “我想要的是获得一个坐标数组,以后可以在我的代码中的 2 个不同位置使用。” 取决于真正的“稍后”意思和什么两个地方。但通常将所有需要数据的代码放在回调中应该有问题。通过更完整的示例,我们可以为您提供更好的帮助。

标签: javascript mysql node.js callback


【解决方案1】:

有几种方法可以解决这个问题,但是如果您希望以与同步代码类似的方式使用生成的坐标,我建议您尝试使用 async/await 语法。如果您从异步函数调用查询,您可以使用 await 关键字来提供更易读的代码。

在异步函数中填充坐标变量后,您可以用它做任何事情。

例如:

function getCoordinates() {
    connection.connect();
    let coordinates = [];
    return new Promise((resolve, reject) => {
        connection.query('SELECT AreaId AS areaNumber, longitude AS longitude, latitude AS latitude FROM coordinates', function (error, results, fields) {
            if (error) {
                reject(error);
            } else {
                let area=[];
                for (result of results) {
                    if (!area.includes(result.areaNumber)){
                        area.push(result.areaNumber)
                        coordinates[result.areaNumber]=[];
                    }
                    coordinate=[result.longitude,result.latitude];
                    coordinates[result.areaNumber].push(coordinate);
                }    
                resolve(coordinates);
                connection.end();
            }
        });
    })
}

async function testCoordinates() {
    let coordinates = await getCoordinates();
    console.log("Coordinates:", coordinates);
    // You can do whatever you wish with the coordinates variable
}

testCoordinates();

【讨论】:

  • console.log("坐标:", 坐标);在异步函数 testCoordinates() 中,我可以返回坐标,对吗?因为那样它会返回给我 {promise}
  • 当你使用await关键字时,你不会得到一个promise,你会得到坐标数组!它与使用 .then() 非常相似,只是语法不同(恕我直言)更具可读性。
【解决方案2】:

你混淆了不同的东西。让我解释一下

首先,忘记“回调”这个词。我们将把它简单地理解为一个函数作为参数传递给另一个函数。不用担心,我会解释的。

所以我们从问题开始:如何从数据库中获取坐标数组并打印出来

接下来您将了解与数据库交互并假设这是您的库的工作方式:它有一个函数 connection.query(myQuery, someFunction) 可以从数据库中获取结果。

现在,关于该查询函数,您首先注意到的是它的参数。 myQuery 是一个字符串,someFunction 是一个函数定义。虽然我们在其他语言中看到我们将数值、字符串等值作为参数传递给函数,但有趣的是,在 javascript 中,您也可以将函数定义作为参数传递。这就是 javascript 强大的原因,您可以将函数定义作为参数传递。

哇,将函数作为参数传递,但它是如何工作的? 让我们举一个不同的例子;假设我想创建一个函数来进行一些计算

//Here myVar is a variable and doSomething is a function
function interestingJsFunction(myVar, doSomething){
   var twiceOfVar = 2*myVar;
   doSomething(twiceOfVar);
   var thriceOfVar = 3*myVar;
   doSomething(thriceOfVar);
}

那么这个函数有什么作用呢?它应用一些计算并在某些点调用函数 doSomething;在某一时刻,它将 myVar 的两倍值传递给函数,在某一时刻,它传递 myVar 的三次。但是这个函数doSomething 的作用目前还没有定义。您可以通过将自己的函数作为参数传递来定义它。这给了你无限的可能性。怎么样?

interestingJsFunction(2, function(result){ console.log(result) })
interestingJsFunction(2, function(result){ console.log("the new behaviour" + result) })
//And so on...

您了解这如何使一个有趣的JsFunction 在不同的情况下有用。如果你知道你只需要一个 doSomething() 的实现,你可以简单地从参数中删除doSomething 并且无论你想要做什么,你都可以直接在interestingJsFunction 中完成(例如console.log(twiceOfVar))

现在回到query 函数。这也是一个有趣的JsFunction,如果你去定义query 函数(你可以挖掘你的库的代码来看看查询函数里面有什么)你会发现它对数据库做了一些操作,得到结果并调用作为参数传递的函数[类似于doSomething],如果在这样做时遇到任何错误,它会调用参数中的函数并将错误传递给它。

当它这样做时,它会将错误、结果和字段作为参数发送回此函数。因此,现在您可以在作为参数传递的函数中使用这些参数。现在由您决定如何使用它,您可以简单地打印结果如下(还记得我们如何使用interestingJsFunction吗?)

//We could simply print the results if there are any
connection.query(myQuery, function(error, results, fields){ 
    if(results) console.log(results.toString()) 
})

//Or we could throw erorr if there's any
connection.query(myQuery, function(error, results, fields){ 
    if(error) throw error 
})

//Guess what would happen when you return something inside this function?
var myQueryFunction = connection.query(myQuery, function(error, results, fields){ 
    return "return of query function"
})


//And guess what would happen when you have something like this
function myNewFunction(){
    var x = connection.query(myQuery, function(error, results, fields){ 
        return "return of query function"
    })
    return "return of myNewFunction"
}

我将最后 2 个练习留给您,之后您应该能够解决问题。试试 console.log 语句来理解

【讨论】:

  • 一些反馈:总体正确,但是选词有点不准确。例如:“有趣的是,您不仅可以将变量作为参数传递,还可以将函数作为参数传递。” 您在这里混合了两件事:函数是 value 在 JavaScript 中,variable 基本上是一个包含 value 的容器。你在比较苹果和橘子。变量实际上不是作为参数传递的,而是值。变量只是提供该值的一种方式(评估变量会产生它所持有的值)。
  • 没错。 @felix-kling 感谢您的纠正。立即编辑。
猜你喜欢
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-02
  • 2018-03-07
  • 1970-01-01
相关资源
最近更新 更多