为什么它对数组有效但对象不正确?

时间:2015-07-01 07:36:23

标签: javascript angularjs

我正在开发AngularJS应用程序并发现以下行为。

我的服务中有两个功能。第一个函数返回存储在数据库中的所有类别,第二个函数按其id返回一个类别。

这是我的服务:

angular.module('categoriesRepository', [])
.service('categoriesRepository', ['$cordovaSQLite', 'sqliteHelper',
    function ($cordovaSQLite, sqliteHelper) {
        //this works - returns an array with all categories
        this.getAll = function () {
            var categories = [];

            $cordovaSQLite.execute(sqliteHelper.getDb(),
                "SELECT * FROM categories;")
                .then(function (res) {
                    for (var i = 0; i < res.rows.length; i++) {
                        categories.push(res.rows[i]);
                    }
                });

            return categories;
        }

        //this works not - returns undefined
        this.getById = function (id) {
            var category;

            $cordovaSQLite.execute(sqliteHelper.getDb(),
                "SELECT * FROM categories WHERE id = ?;", [id])
                .then(function (res) {
                    category = res.rows[0];
                });

            return category;
        }

    }]);

我知道我可以使用Angulars $ q异步运行函数,并在完成处理时使用它们的值。

为什么getById函数直接返回类别而getAll会等到数组被填满?

修改

我发布了错误的getAll函数。 $ cordovaSQLite.execute

之前没有返回语句

4 个答案:

答案 0 :(得分:2)

<强>更新: - 您的问题更新后。

  1. 在第一个示例中,您首先通过执行var categories = [];创建数组,然后在完成异步调用之前返回此数组。异步调用完成后,只需将某些元素推入数组,因此会破坏对数组(categories)变量的引用。如果你将调试它返回它,你会发现该函数返回一个空数组,稍后当异步调用成功时,数组将被填充。

  2. 在第二个示例中,您只创建一个变量,然后在异步调用完成之前返回它。但是,异步调用完成后,您将变量赋值给新值。从而摧毁了之前的参考文献。

  3. <强>解决方案: - 虽然不是一种让它有效的优先方法。您将不得不维护类别变量引用。为此,您可以使用angular.copyangular extend

    所以代码的第二部分应该是这样的 this.getById = function(id){             var category;

            $cordovaSQLite.execute(sqliteHelper.getDb(),
                "SELECT * FROM categories WHERE id = ?;", [id])
                .then(function (res) {
                    angular.copy(res.rows[0], category);
                    //now the reference to the category variable 
                    //will not be lost
                });
    
            return category;
        }
    

    更好的实践: - 你开发这个应用程序的方式是错误的。异步调用不应该以这种方式处理。我之前提出了一个问题,只是为了澄清在角度应用程序,工厂和控制器please have a look here内处理异步调用和状态的方法。它提供了两种处理状态和异步调用的方法。可能会有更多的实践,但这两个最适合我。

答案 1 :(得分:2)

不幸的是,这种方法出现来工作&#39;因为它是由修改返回的数组对象&#34;在某个未指定的时间&#34;在之后返回

1 1 之后,在异步调用中修改了访问/观察的数组。这使得它似乎只能正常运行 ,因为(意外)异步 - 晚于观察。

如果观察是在实际完成SQLite操作之前 - 例如在getAll函数调用之后立即执行 - 它将显示数组。

两个函数都写得不正确,第一个意外releases Zalgo(或许是他的兄弟姐妹)。

有关详细信息,请参阅How do I return the response from an asynchronous call?

1 Chrome的console.log可能会让人感到困惑,因为它像console.dir一样工作,因此可能会显示当前值,而不是显示的值被援引。

答案 2 :(得分:1)

如上所述,这是一种糟糕的做法。返回后不能立即使用函数的结果。

但是,我没有看到您确切问题的答案:为什么他们的行为不同

这是因为使用数组返回对象的引用(类型为Array)。稍后您使用相同的引用来修改对象的内容,即将新项目推送到数组中。

但是在第二个函数中,您可以修改引用本身。你让局部变量categories指向一个新对象。因此,旧对象(返回到外部范围的引用)保持不变。使它的工作方式与你应该写的相同

category.row = res.rows[0];

答案 3 :(得分:0)

在第一种情况下返回执行结果,而在第二种情况下返回该变量,该变量很可能尚未填充。