【问题标题】:Why does my Promise Chain not work in this nested way?为什么我的 Promise Chain 不能以这种嵌套方式工作?
【发布时间】:2016-01-31 09:19:50
【问题描述】:

我想像这样在 Promise 链中链接 4 个函数:

函数1->函数2->函数3->函数4

我的承诺链

if ($location.$$url !== "/dashboard") {
    vm.customURL = true;
    // (1) Set root vars & Rebuild tickerTagsContainer:
    var promise = TagFactory.buildUrlObject($location.$$url).then(function() {
        console.log('TagFactory.buildUrlObject PROMISE returned');
    }).then(function() {
        console.log('(2) Re-display tags in viewHeader');
        // (2) Re-display tags in viewHeader:
        viewHeader = ScopeFactory.getScope('viewHeader');
        viewHeader.vh.displayViewHeaderTags().then(function() {
            console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
        });
    }).then(function() {
        // (3) Reselect timeSpan:
        console.log('(3) Reselect timeSpan');
        viewHeader.vh.toggleTimeSpan(vm.timeSpan);
        // (4) Refresh URL:
        console.log('(4) Refresh URL');
        ViewFactory.remakeViewObject($location.$$url);
    });
}

生成的 console.logs:

^ 请注意,我从未见过此日志:

viewHeader.vh.displayViewHeaderTags().then(function() {
    console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
});

理想情况下,我想将我的 (3) 函数放在其中,然后像这样链接我的 (4):

viewHeader.vh.displayViewHeaderTags().then(function() {
    console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
    console.log('(3) Reselect timeSpan');
    viewHeader.vh.toggleTimeSpan(vm.timeSpan).then(function() {
        console.log('(4) Refresh URL');
        ViewFactory.remakeViewObject($location.$$url);
    });
});

但是我从来没有从.then 函数中看到console.log displayViewHeaderTags


这是我的displayViewHeaderTags 的样子:

function displayViewHeaderTags() {
    vm.viewTickerTags = [];
    vm.viewTickerTags = TagFactory.retrieveTickerTags('all');

    var deferred = $q.defer();
    var tikObjs  = vm.viewTickerTags.map(function(el) { return el.ticker; });
    var tagObjs  = vm.viewTickerTags.map(function(el) { return el.tags; });
    var tags     = _.flatten(tagObjs);

    // forEach loops up to 3 times:
    tags.forEach(function(tag, i) {
        vm.viewTags = [];
        ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
            vm.viewTags.push(data.data.ticker_tag);
            if (i === tags.length) {
                deferred.resolve();
            }
        });
    });

    return deferred.promise;
}

在我的 displayViewHeaderTags 函数中,我遇到了一个循环,该循环最多运行 3 次,在完成获取数据后,它将填满并调用 deffered.resolve。然后返回它return deferred.promise;

那么为什么我从来没有看到这个日志? console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');

【问题讨论】:

标签: javascript angularjs promise angular-promise


【解决方案1】:

您的 i 永远不会与长度相同,因为 i 变量从零开始(数组索引从零开始)。这意味着如果您有一个长度 = 2 的数组,您的 i 值将分别为 0 和 1。它永远不会等于零。基本上,您希望条件是:

vm.viewTags.push(data.data.ticker_tag);
if (i + 1 === tags.length) {
    deferred.resolve();
}

无论如何,使用defer()code smell

更优雅的做法是使用$q.all

var allPromises = [];
var promise;
tags.forEach(function(tag) {
    vm.viewTags = [];
    promise = ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
        vm.viewTags.push(data.data.ticker_tag);
    });

    // Create an array of promises, one promise for each request
    allPromises.push( promise );
});

// Return a new promise that will only be resolved 
// when all the promises of the array `allPromises` are resolved,
// or is rejected when one of them is.
return $q.all( allPromises );

【讨论】:

  • 啊!谢谢,那是我的问题...我现在看到了我试图捕获的日志,并且可以继续嵌套的 Promise 链。
【解决方案2】:

您的链实际上并没有做任何事情,因为您没有从任何匿名函数返回承诺。您没有看到该日志可能是因为 ApiFactory.getTagDataSilm 失败或永远无法解决。尝试将错误处理程序添加到您的流程中。

if ($location.$$url !== "/dashboard") {
    vm.customURL = true;
    // (1) Set root vars & Rebuild tickerTagsContainer:
    var promise = TagFactory.buildUrlObject($location.$$url).then(function() {
        console.log('TagFactory.buildUrlObject PROMISE returned');
    }).then(function() {
        console.log('(2) Re-display tags in viewHeader');
        // (2) Re-display tags in viewHeader:
        viewHeader = ScopeFactory.getScope('viewHeader');
        return viewHeader.vh.displayViewHeaderTags().then(function() {
            console.log('viewHeader.vh.displayViewHeaderTags FINISHED!');
        });
    }).then(function() {
        // (3) Reselect timeSpan:
        console.log('(3) Reselect timeSpan');
        return viewHeader.vh.toggleTimeSpan(vm.timeSpan);
    }).then(function() {
        // (4) Refresh URL:
        console.log('(4) Refresh URL');
        return ViewFactory.remakeViewObject($location.$$url);
    }).catch(function(error) {
        console.log('Something failed', error);
    });
}

displayViewHeaderTags 内,您可以使用$q.all,以便为您处理拒绝:

// forEach loops up to 3 times:
vm.viewTags = [];

return $q.all(_.map(tags, function(tag) {
    return ApiFactory.getTagDataSilm(tag.term_id).then(function(data) {
        vm.viewTags.push(data.data.ticker_tag);
    });
}));

【讨论】:

  • ?不跟随,我从 buildUrlObject 返回一个 Promise,这就是它能够继续链条的方式。在displayViewHeaderTags 中,我也以同样的方式返回了一个 Promise ......但是它不会在它的嵌套链中继续。我的ApiFactory 调用没有错误,但我添加了捕获。
  • @LeonGaban - 研究 Anid 的代码并查看他们添加的所有其他地方 return 语句以从 .then() 处理程序中返回承诺。
  • 使用$q.all查看我修改后的代码。你的 if 语句永远不会被击中,因为 i 的上限是 length - 1
  • 谢谢!我需要returns$q.all
猜你喜欢
  • 1970-01-01
  • 2017-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-07
  • 2018-01-01
  • 2013-04-11
  • 2013-03-04
相关资源
最近更新 更多