【问题标题】:Confirmed populated array of objects returns empty确认填充的对象数组返回空
【发布时间】:2018-04-09 20:06:23
【问题描述】:

我有一个方法在返回对象数组时失败。如标题中所述 - 该数组已确认已填充,但在响应中为空。

这是完整的流程:

网址:

http://localhost:53000/api/v1/landmarks?lat=40.76959&lng=-73.95136&radius=160

被路由到对应的索引:

api.route('/api/v1/landmarks').get(Landmark.list);

索引调用服务:

exports.list = (req, res) => {

    const landmark = new LandmarkService();

    landmark.getLandmarks(req)
        .then(landmarks => {

            var response = new Object();
                response.startindex = req.query.page;
                response.limit = req.query.per_page;
                response.landmarks = landmarks;

                res.json(response);
        })
        .catch(err => {
            logger.error(err);
            res.status(422).send(err.errors);
        });
};

服务方法使用数据访问类返回承诺

  getLandmarks(req) {

    const params = req.params || {};
    const query = req.query || {};
    const page = parseInt(query.page, 10) || 1;
    const perPage = parseInt(query.per_page, 10);
    const userLatitude = parseFloat(query.lat); 
    const userLongitude = parseFloat(query.lng); 
    const userRadius = parseFloat(query.radius) || 10;
    const utils = new Utils();
    const data = new DataService();
    const landmarkProperties = ['key','building','street','category','closing', 
            'email','name','opening','phone','postal','timestamp','type','web'];

    return data.db_GetAllByLocation(landmarksRef, landmarkLocationsRef, 
                    landmarkProperties, userLatitude, userLongitude, userRadius);

    } // getLandmarks

但是,响应总是空的。

我正在被调用的方法中构建一个数组并用 JSON 对象填充它。这就是应该在响应中发回的内容。在点击返回语句之前,我可以确认属性数组已正确填充。我可以将它记录到控制台。我还可以成功发回一个充满存根值的测试数组。

我有一种感觉,这就是我在 Promise 中的设置方式?

应该返回对象数组的数据访问方法:

db_GetAllByLocation(ref, ref_locations, properties, user_latitude, user_longitude, user_radius)
{  
        const landmarkGeoFire = new GeoFire(ref_locations);
        var geoQuery = landmarkGeoFire.query({
                center: [user_latitude, user_longitude], 
                radius: user_radius
        });
        var locations = [];
        var onKeyEnteredRegistration = geoQuery.on("key_entered", function (key, coordinates, distance) {
            var location = {}; 
                location.key = key;
                location.latitude = coordinates[0];
                location.longitude = coordinates[1];
                location.distance = distance;
                locations.push(location);                   
        });
        var attributes = [];
        var onReadyRegistration = geoQuery.on("ready", function() {
            ref.on('value', function (refsSnap) {
                   refsSnap.forEach((refSnap) => {
                        var list = refSnap;
                        locations.forEach(function(locationSnap) 
                        {
                            //console.log(refSnap.key, '==', locationSnap.key);
                            if (refSnap.key == locationSnap.key) 
                            {
                                var attribute = {};
                                for(var i=0; i<=properties.length-1; i++)
                                {
                                    if(properties[i] == 'key') {
                                        attribute[properties[i]] = refSnap.key;
                                        continue;
                                    }
                                    attribute[properties[i]] = list.child(properties[i]).val();
                                }
                                attribute['latitude'] = locationSnap.latitude;
                                attribute['longitude'] = locationSnap.longitude;
                                attribute['distance'] =  locationSnap.distance;
                                attributes.push(attribute);    
                            } // refSnap.key == locationSnap.key
                        }); // locations.forEach
                    }); // refsSnap.forEach
                    return Promise.resolve(attributes); <-- does not resolve (throws 'cannot read property .then')
                  //geoQuery.cancel();
                }); // ref.on
        }); // onreadyregistration      

        return Promise.resolve(attributes); <-- comes back empty
}

【问题讨论】:

  • 我在你的代码中看不到任何承诺。
  • 是的,我把周围的代码用于调用,因为我没有发现它相关,但是 - 你永远不知道。我已根据您的建议添加了它。
  • 似乎更新的代码没有发布,现在我要回家了。我回家后会发帖。但是,我可以确认调用代码包含在一个 Promise 中。
  • 除了看到promise之外,以后最好把console.log语句全部去掉,让理解起来更快
  • 看来我可能需要兑现我的承诺——像这样:javascript.info/promise-chaining

标签: javascript node.js associative-array


【解决方案1】:

似乎 data.db_GetAllByLocation 是一个异步函数,因此调用 resolve(landmarks);在异步函数执行完成之前被调用。如果 data.db_GetAllByLocation 返回一个 promise,则在 promise 中调用 resolve(landmarks)。

data.db_GetAllByLocation().then(function() {
  resolve();
})

还可以尝试以下修改后的 db_GetAllByLocation()

db_GetAllByLocation(ref, ref_locations, properties, user_latitude, user_longitude, user_radius)
    {       
			return new Promise(function(resolve, reject){
				const landmarkGeoFire = new GeoFire(ref_locations);

				var geoQuery = landmarkGeoFire.query({
						center: [user_latitude, user_longitude], 
						radius: user_radius
				});

				var locations = [{}];

				var onKeyEnteredRegistration = geoQuery.on("key_entered", function (key, coordinates, distance) {
					var location = {}; 
						location.key = key;
						location.latitude = coordinates[0];
						location.longitude = coordinates[1];
						location.distance = distance;
						locations.push(location);                   

				});

			   var attributes = [{}];

				var onReadyRegistration = geoQuery.on("ready", function() {
					ref.on('value', function (refsSnap) {
						   refsSnap.forEach((refSnap) => {
								var list = refSnap;
								locations.forEach(function(locationSnap) 
								{
									if (refSnap.key == locationSnap.key) 
									{
										var attribute = {};
										for(var i=0; i<=properties.length-1; i++)
										{
											if(properties[i] == 'key') {
												attribute[properties[i]] = refSnap.key;
												continue;
											}
											attribute[properties[i]] = list.child(properties[i]).val();
										}

										attribute['latitude'] = locationSnap.latitude;
										attribute['longitude'] = locationSnap.longitude;
										attribute['distance'] =  locationSnap.distance;
										attributes.push(attribute);    
									} // refSnap.key == locationSnap.key
								}); // locations.forEach
							}); // refsSnap.forEach
							// return JSON.stringify(attributes);
							return resolve(attributes);
						}); // ref.on
				

				}); // onreadyregistration

			});
            
} 

【讨论】:

  • 嗨tejzpr。我认为您是对的,这是执行顺序问题。但是, db_GetAllByLocation 不返回承诺。它返回一个对象数组。调用方法 - getLandmarks - 确实返回了一个承诺,我在那里有 resolve()。但我认为你是对的,resolve(landmarks) 在异步函数的堆栈执行完成之前被调用。
  • 另一个原因可能是 userLatitude 或 userLongitude 的 parseFloat() 可能评估为 NaN。进而将流发送到“else”块,因此地标可能显示默认值;]
  • 再次感谢。我很感激你的回复。不幸的是,事实并非如此。我可以确认参数正在通过。
  • 谢谢,晚饭后我试试修改方法。我有一种预感,将所有承诺逻辑从服务类(getLandmarks 所在的位置)转移到持久性方法可能会起作用。晚饭后我会跟进并通知您,再次感谢。
  • 是的,只要内部承诺正确解决,就可以链接承诺。
【解决方案2】:

好的,我通过删除所有代码并编写一些测试逻辑来对此进行排序(我应该在发布问题之前完成此操作)。

以下流程适用于我,并且应用回我的代码,为我提供了我正在寻找的结果。无需重新发布代码,但也许下面的流程会对某人有所帮助。

路线

api.route('/api/v1/landmarks').get(Landmark.test);

索引

exports.test = (req, res) => {

    const landmark = new LandmarkService();

    landmark.getLandmarksTest(req)
        .then(landmarks => {
            var final = {};
                final.attr1 = 'attr1';
                final.attr2 = 'attr2';
                final.landmarks = landmarks;
                res.json(final);
        })
        .catch(err => {
            logger.error(err);
            res.status(422).send(err.errors);
        });

};

服务方式

getLandmarksTest(req)
{

            const data = new DataService();

        data.db_PromiseTest().then(results => {
                return Promise.resolve(results);
          }).catch(err => {
                return Promise.reject(err.errors);
          });




}

数据层法

db_PromiseTest()
{
            var stub = {
                    "name": "Madame Uppercut",
                    "age": 39,
                    "secretIdentity": "Jane Wilson",
                    "powers": [
                    "Million tonne punch",
                    "Damage resistance",
                    "Superhuman reflexes"
                    ]
                };

                 return Promise.resolve(stub);


}

【讨论】:

  • 避免getLandmarksTest中的Promise constructor antipattern!同样在db_PromiseTest 中,您可以只使用return Promise.resolve(…);
  • 谢谢,@Bergi - 解决这个问题。我最初收到 TypeError: Cannot read property '.then' of undefined in the Index file.
  • 很可能您只是在正确的位置忘记了return
  • 嗯。更新了我的代码。有什么预感吗?
  • 是的,正如我猜的那样,getLandmarksTest 函数中没有 return。 (回调中的那些不算)。而且你根本不需要那些 .then(Promise.resolve).catch(Promise.reject) 的东西,它们不会做任何原始承诺没有做的事情 - 只需返回 data.db_PromiseTest() 结果并完成它。
猜你喜欢
  • 2020-11-24
  • 2020-11-11
  • 2016-12-04
  • 1970-01-01
  • 2017-10-26
  • 2016-04-28
  • 2018-06-04
  • 1970-01-01
  • 2021-01-02
相关资源
最近更新 更多