【问题标题】:Promise .then chain 'Cannot set property of undifined'Promise .then 链'不能设置未定义的属性'
【发布时间】:2017-11-09 19:47:11
【问题描述】:

我是 Promise 的新手,无法解决这个语法问题。

在我的 .then 链中,我得到了这部分。我得到了一个数组数组,并想为数组内的一些对象添加一个新属性。在我这样做之前,我必须向googleDirection.directionRequest 提出请求,这解决了一个新的承诺。但是在将属性添加到对象时,对象是未定义的(我认为是因为 dbResult 在嵌套的 .then 链之外)。我尝试了不同的方法,但无法解决这个问题。 希望有人能帮我解决这个问题。

.then(function(dbResult){
    for(var i = 0; i < dbResult.length; i++){
      if(dbResult[i].length == 1){            
        var smallestValue = googleDirection.directionRequest(req.body.startlocation.latitude + "," + req.body.startlocation.longitude, dbResult[i][0].startLatitude + "," + dbResult[i][0].startLongitude,true,false)
        .then(function(distance){
          var values = [];
          for(var j = 0; j < distance.routes.length; j++){
            values.push(distance.routes[j].legs[0].distance.value);
          }
          return Math.min.apply(Math,values);
        })
        .then(function(value){
          return(value);
        })
        smallestValue.then(function(value){
          dbResult[i].tripStartToConstrStart = value;
        })
      }
    }
    return dbResult
  })
.then(function(dbResult){
    console.log(dbResult)       
  })

这是错误:

(node:8708) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot set property 'tripStartToConstrStart' of undefined
(node:8708) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): TypeError: Cannot set property 'tripStartToConstrStart' of undefined

【问题讨论】:

  • 如果要等待多个方向请求,需要Promise.all
  • 您也可以使用let i 而不是var i,但是@Bergi 的评论当然是正确的(let i 只会解决dbResult[i].tripStartToConstrStart = value 错误,因为i 将是用let定义时的当前索引)
  • 但问题不在于请求,问题在于我无法在嵌套的.then 中向dbResult[i] 添加新属性。 dbresult[i] 未定义。但在外观之外它工作正常
  • @WeSt 问题是您没有等待循环中的请求在检查结果之前添加结果
  • @WeSt 这个错误是因为i is having the wrong value in the callback closures,正如 Icepickle 正确观察到的那样

标签: javascript node.js promise


【解决方案1】:

您的错误原因被称为“关闭循环变量”,这意味着当异步操作完成并尝试执行时

dbResult[i].tripStartToConstrStart = value;

idbResult.length 具有相同的值(因为此时for 循环已经完成),而dbResult[dbResult.length]undefined

正如评论者已经说过的,您可以使用let 来解决这个问题,但我认为使用返回您需要等待的所有承诺的map 会更容易(因为不等待承诺是另一个您的代码中的大问题):

.then(function(dbResult) {
    var startLocation = req.body.startLocation; 

    var promises = dbResult.map(function(item) {
        if (item.length !== 1) {
            return item;
        }

        return googleDirection.directionRequest(
            startlocation.latitude + "," + startlocation.longitude, 
            item[0].startLatitude + "," + item[0].startLongitude, 
            true, 
            false
        ).then(function(distance) {
            var distances = distance.routes.map(function (route) {
                return route.legs[0].distance.value;
            });

            item.tripStartToConstrStart = Math.min.apply(Math, distances);

            return item;
        });
    });

    return Promise.all(promises);
})
.then(function(dbResult) {
    console.log(dbResult)
})

【讨论】:

  • 这就像一个魅力。非常感谢。我喜欢你的代码风格,你有像你这样的 JS/nodeJS 设计模式的好资源吗?
  • @WeSt 这只是我一路上学到的东西,但一些经验法则是:(1)为数组中的每个项目执行异步操作,使用Promise.all(arr.map(....))(2 ) 将数组中的每个值转换为另一个值并创建一个包含新值的数组,使用 arr.map(...) (3) 仅迭代数组,使用 arr.forEach(....) (4) 仅在 (2) 时使用 for(; ;) (3) 不太适合这项任务。
【解决方案2】:

那是因为异步任务,i总是在请求返回结果之前递增。

您可以在

中使用let i = 0

```

for(var i = 0; i < dbResult.length; i++){

```

或者你可以使用闭包来解决problem

想想这三个例子:

for (var i =0; i< 10;i++) {
  setTimeout(function(){
    console.log(i);
  },10)
}

for (let i =0; i< 10;i++) {
  setTimeout(function(){console.log(i)},10)
}

for(var i = 0; i < 10; i++){
  (function(j) {
    setTimeout(function() {
      console.log(j)
    }, 10);
  })(i);
}

【讨论】:

    猜你喜欢
    • 2014-11-26
    • 2018-01-15
    • 2018-06-02
    • 1970-01-01
    • 2021-04-17
    • 2017-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多