【问题标题】:Angular nested loop promises角嵌套循环承诺
【发布时间】:2017-02-15 01:20:41
【问题描述】:

我正在尝试使用SQLite plugin 构建一个 Cordova/angular 应用程序以使其脱机工作,但我在查询数据库的承诺和异步方面遇到了很多麻烦这是我的情况:

我有 5 个这样配置的表:

带有 id、title、template_id 和 body 的表格页面 带有 id 和标签的表格菜单 表 PageMenus 与 id 和 page_id 和 menu_id 关联页面和菜单 具有 id menu_id 和 body 的表 MeniItems 在教学菜单中具有“实际”元素 带有id和标签的表格模板选择正确的视图

出于兼容性原因(我对 web 应用程序和移动应用程序使用相同的代码,对于 web 应用程序我在移动设备上调用我的 API 我下载设备上的所有内容)我需要以这种格式检索页面:

{
  "id": 1
  "body": "Welcome to the homepage",
  "title": "Homepage",
  "template_tag": "tab",
  "menus": [
    {
      "id": 3,
      "tag": "home_menu",
      "menu_items": [
        {
          "menu_id": 3,
          "body": "Movie"
        },
        {
          "menu_id": 3,
          "body": "Restaurant"
        },
        {
          "menu_id": 3,
          "body": "Messages"
        },
        {
          "menu_id": 3,
          "body": "Systems"
        }
      ]
    },
    {
      "id": 62,
      "tag": "user_menu",
      "menu_items": [
        {
          "menu_id": 62,
          "body": "About"
        },
        {
          "menu_id": 62,
          "body": "Updates"
        },
        {
          "menu_id": 62,
          "body": "Help"
        },
        {
          "menu_id": 62,
          "body": "Reset Password"
        },
        {
          "menu_id": 62,
          "body": "Report/ Feedback"
        }
      ]
    }
  ]
}

我已经能够获得正确的格式,但我的问题是控制器在解决之前尝试访问菜单的主体,所以我得到错误未定义这是我目前在工厂使用的代码:

return {
  getHomePage: function() {
    // other function
  },
  getPage: function(id) {
    var results = $q.defer();

    function request() {
      var res = {};
      var queryPage = "SELECT pages.id, pages.body, pages.title, templates.tag AS template_tag FROM pages JOIN templates ON pages.template_id = templates.id WHERE pages.id = ?";
      $cordovaSQLite.execute(db, queryPage, [id]).then(function(page) {
        res = page.rows.item(0);
        res.menus = [];
        var queryMenus = "SELECT menus.id, menus.tag FROM menus JOIN page_menus ON menus.id = page_menus.menu_id WHERE page_menus.page_id = ?";
        $cordovaSQLite.execute(db, queryMenus, [res.id]).then(function(menus) {
          for (var i = 0; i < menus.rows.length; i++) {
            var menu = {
              id: menus.rows.item(i).id,
              tag: menus.rows.item(i).tag,
              menu_items: []
            };
            var queryMenuItems = "SELECT * FROM menu_items JOIN menus ON menu_items.menu_id = menus.id where menus.id = ?"
            $cordovaSQLite.execute(db, queryMenuItems, [menus.rows.item(i).id]).then(function(menu_items) {
              for (var i = 0; i < menu_items.rows.length; i++) {
                menu.menu_items.push(menu_items.rows.item(i));
              }
            });
            res.menus.push(menu);
          };
          results.resolve(res);
        });
      });
    };
    request();

    return results.promise;
  },
  getMedia: function(id) {
    // other function
  }
};

【问题讨论】:

  • 如果您使用的是ui-router,请查看resolve。问题是您在数据不可用时请求数据,resolve 方法允许您在加载状态之前加载数据,以确保您拥有您期望的数据。只看代码很难定义你的上下文。

标签: angularjs sqlite api cordova promise


【解决方案1】:

以这种方式链接 promise 是一种很好的做法:

getSomething: function(...) {
    return requestReturningPromise1.then(function(resultOfPromise1) {
        // Do something here (prepare next request,...)
        return requestReturningPromise2;
    }).then(function(resultOfPromise2) {
        // Do something here (prepare next request,...)
        return requestReturningPromise3;       
    }).then(function(resultOfPromise3) {
        // Do something here (prepare next request,...)
        return finalReturn;       
    });
}

嵌套减少,可读性更高且易于调试。 所以将它应用到你的代码中会得到这样的结果:

getPage: function(id) {
    var res = {};
    var queryPage = "SELECT pages.id, pages.body, pages.title, templates.tag AS template_tag FROM pages JOIN templates ON pages.template_id = templates.id WHERE pages.id = ?";
    return $cordovaSQLite.execute(db, queryPage, [id]).then(function(page) {
        res = page.rows.item(0);
        res.menus = [];
        var queryMenus = "SELECT menus.id, menus.tag FROM menus JOIN page_menus ON menus.id = page_menus.menu_id WHERE page_menus.page_id = ?";
        return $cordovaSQLite.execute(db, queryMenus, [res.id]);
    }).then(function(menus) {
        var menuPromises = [];
        for (var i = 0; i < menus.rows.length; i++) {
            var menu = {
                id: menus.rows.item(i).id,
                tag: menus.rows.item(i).tag,
                menu_items: []
           };
            var queryMenuItems = "SELECT * FROM menu_items JOIN menus ON menu_items.menu_id = menus.id where menus.id = ?";
            var menuPromise = $cordovaSQLite.execute(db, queryMenuItems, [menus.rows.item(i).id]).then(function(menu_items) {
                for (var i = 0; i < menu_items.rows.length; i++) {
                    menu.menu_items.push(menu_items.rows.item(i));
                }
                return menu;
            });    
           menuPromises.push(menuPromise);                        
        }
        return Promise.all(menuPromises);        
    }).then(function(menus) {
        for (var i = 0; i < menus.length; i++) {
            res.menus.push(menus[i]);
        }
        return res;
    });
}

请注意,在上面的代码中,服务本身会返回一个承诺,因此您必须在控制器中像这样使用它:

MyService.getPage(id).then(function(page) {
     // here, bind the result page to your controller scope ...
});

【讨论】:

    【解决方案2】:

    这就是我最终要做的事情(即使我认为对其他人没有真正帮助,我也会发布它,因为它很大程度上基于我的数据库的结构):

    var promisePage = $cordovaSQLite.execute(db, "SELECT p.*, t.tag AS template_tag FROM pages AS p JOIN templates AS t ON p.template_id = t.id WHERE p.id = ?", [id]);
    var promiseMenus = $cordovaSQLite.execute(db, "SELECT m.* FROM menus AS m JOIN page_menus AS pm ON m.id = pm.menu_id WHERE pm.page_id = ?", [id]);
    var promiseMenuItems = $cordovaSQLite.execute(db, "SELECT mi.* FROM menu_items AS mi JOIN menus AS m ON mi.menu_id = m.id JOIN page_menus AS pm ON pm.menu_id = m.id WHERE pm.page_id = ?", [id]);
    
    return $q.all([promisePage, promiseMenus, promiseMenuItems]).then(function(data) {
      var page = data[0].rows.item(0);
      var menus = data[1];
      var menuItems = data[2];
      // here there is some boring code to construct the page
      return page;
    

    获得页面后,我无需在数据库中查询菜单和菜单项,而是并行查询所有三个元素,然后在 $q.all 中完成所有工作。

    【讨论】:

      猜你喜欢
      • 2019-01-23
      • 1970-01-01
      • 2016-08-20
      • 1970-01-01
      • 2019-02-24
      • 1970-01-01
      • 2017-06-22
      • 2019-12-25
      • 2014-10-27
      相关资源
      最近更新 更多