我的服务使用名为“getGmapsDistance()
”的方法。在这里我使用谷歌地图api来获得原点和目的地之间的距离。
export default Ember.Service.extend({
getShortestDistanceInMeters: function(location) {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix({
...
}, this.callback); //<<<<<< !!!
},
callback: function(response, status) {
....
}
});
在我的控制器中,如果得到一个包含位置的数组,现在我想迭代它并想要检查每个元素,如果距离是&lt; =最大目的地。
locationsNearby: Ember.computed('locations', function() {
//...
var filteredResult = [];
locations.forEach(function(locat) {
if (this.get('distanceService').getShortestDistanceInMeters(locat) <= maxDistance) {
filteredResult.pushObject(locat);
}
});
return filteredResult;
})
不幸的是,用于距离计算的GMaps API使用回调,因此请求是异步的。
我该如何解决这个问题?
答案 0 :(得分:2)
您不能同步进行异步调用!这是一个javascript语言限制,很重要的是要理解! Javascript只有一个帖子,所以这不能被图书馆改变!
用于处理回调的花哨的新方式是Promises。 您真的非常应该结帐specifications! 这是你读过的最漂亮的规格之一!
Ember大量使用Promise!例如,路由model
hook在继续转换之前等待Promise解析。
在您的情况下,您希望在promise解析时更新计算属性。因为ember-data经常导致这种情况发生,所以它们提供了两个奇特的类:PromiseObject和PromiseArray。取决于返回PromiseObject / Array的计算属性的计算属性将在promise解析时重新计算:
locationsNearby: Ember.computed('locations', {
get() {
let promise = Ember.RSVP.all(this.get('locations').map(location => Ember.RSVP.hash(({
location,
distance: this.get('distanceService').getShortestDistanceInMeters(location)
})))).then(hashs => hashs.filter(hash => hash.distance <= maxDistance).map(hash => hash.location));
return DS.PromiseArray.create({promise});
}
})
稍微解释一下:
我构建了一个数组,其中包含位置的哈希值和对距离的承诺:
let locationsWithDistancePromise = this.get('locations').map(location => {
distance: this.get('distanceService').getShortestDistanceInMeters(location),
location
})
然后我在所有这些上使用RSVP.hash
来获得一系列承诺,这些承诺将解析为具有距离和位置的哈希数组:
let hashPromiseArr = locationsWithDistancePromise.map(h => Ember.RSVP.hash(h));
现在我使用Ember.RSVP.all
获得一个承诺,该承诺将解析为具有位置和距离的哈希数组:
let hashArrPromise = Ember.RSVP.all(hashPromiseArr);
最后我承诺.then
并过滤附近的位置。我还将哈希映射到一个位置数组。
let promise = hashArrPromise.then(hashs => {
return hashs.filter(hash => hash.distance <= maxDistance)
.map(hash => hash.location);
});
将其包装为PromiseArray
return DS.PromiseArray.create({promise});
您可以使用{{#each}}
从车把上循环此计算属性,或在另一个计算属性中使用它:
allNearbyLocations: Ember.computed('locationsNearby.[]', {
get() {
return this.get('locationsNearby').toArray().join(' - ');
}
}
当然你需要重写getShortestDistanceInMeters
以便它返回一个Promise:
getShortestDistanceInMeters(location) {
var service = new google.maps.DistanceMatrixService();
return new Ember.RSVP.Promise((resolve, reject) => {
service.getDistanceMatrix({
//...
}, (response, status) => {
if(status.error) {
reject(response);
} else {
resolve(response);
}
});
});
}