【问题标题】:Promises issue with async and sync - angular异步和同步的承诺问题 - 角度
【发布时间】:2016-09-07 16:42:19
【问题描述】:

从事移动科尔多瓦/角度项目。下面是一个简单的服务调用:

this.getSomeData = function (businessId) {
    var deferred = $q.defer();
    var query = "SELECT * FROM Stuff";
    $cordovaSQLite.execute(db, query).then(function (res) {
        deferred.resolve(res.rows);
    }, function (err) {
        deferred.reject(err);
    });

    return deferred.promise;
};

问题很简单:

for (var k = 0; k < count; k++) {

    myService.getSomeData($scope.model.stuff[k].id, k).then(function (data) {
        // whatever
    }
);

getSomeData 是异步的,所以当它返回时,for 循环中的k 远不正确。

我想过将k作为参数传递给服务方法:

for (var k = 0; k < count; k++) {

    myService.getSomeData($scope.model.stuff[k].id, k).then(function (data) {
        // whatever
    }
);

并相应地更改服务方法:

this.getSomeData = function (id, index) {
    var deferred = $q.defer();
    var query = "SELECT * FROM Stuff";
    $cordovaSQLite.execute(db, query).then(function (res) {
        deferred.resolve(res.rows, index);
    }, function (err) {
        deferred.reject(err);
    });

    return deferred.promise;
};

但是第二个参数被忽略并且始终未定义。

如何克服这个问题?

【问题讨论】:

  • @JLRishe 你为什么重新打开这个?
  • @Bergi 因为你指出的问题不是这个问题的答案。答案是$q.all()
  • @JLRishe:不,回调中k 的问题通过使用map 其中the closure is created 隐式在您的答案中得到缓解。这与all 无关。
  • @Bergi OP 提供的代码没有循环变量的闭包。 OP 没有向我们展示的代码中可能存在一个问题(文本似乎确实暗示了这一点),虽然对循环变量的闭包可能是这里的根本问题,但我认为最好提供一种惯用的解决方案承诺而不是指示 OP 通过解决关闭问题来拼凑解决方案。

标签: javascript angularjs cordova promise angular-promise


【解决方案1】:

听起来你遇到了一个叫做“循环变量闭包”的问题,这里详细讨论了这个问题:

JavaScript closure inside loops – simple practical example

但是,在您的情况下,干净的解决方案是将Array#map$q.all() 结合起来:

$q.all($scope.model.visits.map(function (stuff) {
    return myService.getSomeData(stuff.id);
})).then(function (results) {
    // results is an array of the results of all the calls to getSomeData() in the correct order
});

另外,正如 Bergi 指出的,避免使用 deferred antipattern

this.getSomeData = function (id) {
    var query = "SELECT * FROM Stuff";

    return $cordovaSQLite.execute(db, query).then(function (res) {
        return res.rows;
    });
};

【讨论】:

  • @chiapa 因为这就是 promise 的工作方式,所以它缩短了 4 行,并且减少了人为错误的空间。
  • 我在 for 循环中做了promisesArray.push(myService.getSomeData($scope.model.stuff[k].id));,所以我将值存储在一个数组中。现在,我想在它们全部运行后访问它们 - 如何?
  • @chiapa: $q.all 已经为您做到了。你检查过它的文档吗?
  • 我试图让它工作,但我得到“TypeError: Object [object Array] has no method 'then'” - 看起来它应该返回一个类似对象的承诺,它有一个then方法
  • @chiapa 我的答案缺少括号。应该是$q.all($scope.model.visits.map(function (stuff) { return myService.getSomeData(stuff.id); }))(函数后面的两个右括号),后面是.then(...)
【解决方案2】:

这就是我让它工作的方式。我尝试使用@JLRishe 的建议,但它不起作用。事实证明,我设法将多个参数传递给服务方法并返回给控制器(通过构建一个包含所需参数的对象)。

myService.getSomeData().then(
    function (stuff) {
        // whatever
    }
).then(function () {
    for (var i = 0; i < $scope.model.stuff.length; i++) {

        // HERE I SEND TWO PARAMETERS TO THE SERVICE METHOD
        myService.getSomeMoreData($scope.model.stuff[i].id, i).then(
            function (data) {
                // whatever
            }
        );
    }
});

this.getSomeMoreData = function (id, index) {
    var deferred = $q.defer();
    var query = "SELECT * FROM stuff";

    $cordovaSQLite.execute(db, query).then(function (res) {
        var moreStuff = [];

        for (var i = 0; i < res.rows.length; i++) {
            var junk = res.rows.item(i);
            moreStuff.push(junk);

        }

        // HERE I RESOLVE AN OBJECT INSTEAD OF TWO PARAMETERS
        deferred.resolve({
            moreStuff: moreStuff,
            index: index
        });

    }, function (err) {
        deferred.reject(err);
    });

    return deferred.promise;
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-07
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    • 2021-05-25
    • 2018-07-01
    • 1970-01-01
    • 2017-12-18
    相关资源
    最近更新 更多