Angular

时间:2015-09-18 13:40:44

标签: angularjs asynchronous service promise synchronous

我有一个链接生成器服务,可以生成指向特定内容类型的链接(用户'详细信息页面,内容项目和详细信息页面等)。

这项服务非常易于使用,并具有同步功能:

links.content(contentInstance); // /items/123
links.user(userInstance); // /users/234

我现在必须为登录用户引入单独的路由,以便从/users/id更改为/users/me

我需要添加到链接生成器服务的唯一更改是检查userInstance.id == loggedInUser.id是否返回不同的路由URL。只要我的登录用户的信息可以同步使用,这就不是问题。但它不是......

我有一个userService.getMyInfo()返回一个承诺。它第一次调用它实际上是一个服务器请求,但后续调用返回已经缓存数据的已解决的承诺。

那么我应该如何在链接生成器服务中实现用户链接URL生成?

修改

确定。所以,为了更好地了解我现在拥有的东西,以及我遇到问题的地方。我非常清楚async会保持异步并且无法转换为同步(并且它不应该)。

这是我的一些代码,这将使其更容易理解。

linkGenerator.user

angular
    .module("App.Globals")
    .factory("linkGenerator", function(userService) {
        ...
        user: function(userInstance) {
            // I should be calling userService.getMyInfo() here
            return "/users/{0}/{1}".format(userInstance.id, userInstance.name);
        },
        ...
    });

userService.getMyInfo

angular
    .module("App.Globals")
    .service("userService", function($q, cacheService, userResource) {
        ...
        getMyInfo: function() {
            if (cacheService.exists("USER_KEY"))
                // return resolved promise
                return $q.when(cacheService.get("USER_KEY"));

            // get data
            return userResource
                .getCurrentUser()
                .$promise
                .then(function(userData) {
                    // cache it
                    cacheService.set("USER_KEY", userData);
                });
        },
        ...
    });

控制器

angular
    .module("App.Content")
    .controller("ItemDetailsController", function(linkGenerator, ...) {
        ...
        this.model = { ... };
        this.helpers = {
            ...
            links: linkGenerator,
            ...
        };
        ...
    });

视图

查看使用ItemDetailsController as context表示法。

...
<a ng-href="{{::context.helpers.links(item.author)}}"
   ng-bind="::item.author.name">
</a>
...

注释

正如您所见,我的视图会生成项目作者的链接。问题是我的linkGenerator(从代码中可以看出,它可能还没有信息是否应该生成一个到用户详细信息视图的正确链接。

我知道我不能(并且不想)将我的异步代码更改为同步代码,但是什么是使这个东西按预期工作的最佳方式?

一种可能的解决方案

目前我已经找到了解决问题的解决方案,但我并不喜欢它,因为我必须提供我登录用户的ID {{{ 1}}功能。然后我设置我的路由,以便将linkGenerator.user(userInstance, loggedInUserId)添加到我调用resolve的路由中,这意味着我的控制器在实例化之前不会被实例化,直到所有的promise都被解析。这条线上的东西:

userService.getMyInfo()

然后我还为控制器添加了一个辅助工具

routeProvider
    .when("...", {
        templateUrl: "path/to/my/details/template",
        controller: "ItemDetailsController".
        controllerAs: "context",
        resolve: {
            myInfo: function(userService) {
                return userService.getMyInfo();
            }
        }
    })
    ...

然后我还通过添加我在视图中提供的附加参数来更改链接生成器的功能。

this.helpers = {
    ...
    links: linkGenerator,
    me: myInfo.id,
    ...
};

并在视图中

linkGenerator.user = function(userInstance, loggedInUserId) {
    if (userInstance.id === loggedInUserId)
        return "users/me";
    return "users/{0}/{1}".format(userInstance.id, userInstance.name);
}

我并不总是提供登录用户的ID。我希望我的服务能够自己处理这些数据。

1 个答案:

答案 0 :(得分:1)

没有办法在JavaScript中做任何异步同步的异步。这是并发工作原理的基本规则 - 不允许阻止等待内容。

相反,您可以让新方法返回一个承诺use the regular tools for waiting for it to resolve

links.me = function(){
     var info = userService.getMyInfo();
     return info.then(info => { // or function(info){ if old browser
          // generate link here
          return `/users/${info.id}`; // or regular string concat if old browser
     });
}

你必须以异步方式使用:

links.me().then(function(link){
    // use link here
});