在非异步上下文中使用回调

时间:2016-02-24 13:05:27

标签: google-maps ember.js

我的服务使用名为“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使用回调,因此请求是异步的。

我该如何解决这个问题?

1 个答案:

答案 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);
            }
        });
    });
}