【问题标题】:JavaScript .map on an array and removing items if condition satisfied数组上的 JavaScript .map 并在满足条件时删除项目
【发布时间】:2014-06-12 14:38:58
【问题描述】:

我有一个数组queue,当它们被修改时,我会将对象推送到它。如果用户按下save,那么我将遍历queue 并为它们应用适当的API 调用。

如果 API 调用成功通过,我想从 queue 中删除该项目,否则将其保留在内部并通知用户某些项目未成功保存。我目前有这个(在 AngularJS 中)

var unsuccessfulItems = [];
var promise = queue.map(function(item) {
    var defer = $q.defer();
    myCallFunction( item
           , function( response ) {} // Success
           , function( response ) {  // Error
               unsuccessfulItems.push(item);
           }
    )
    defer.resolve();
    return defer.promise;
})
// Once all items have been processed
$q.all( promise ).then( function() {
    queue = unsuccessfulItems;
});

有更好的方法吗?

【问题讨论】:

标签: javascript angularjs map iteration promise


【解决方案1】:

您已经在使用 Promise,您可能希望端到端进行。此外,您还为时过早解决了承诺。

假设您不想承诺myCallFunction 本身的次优情况,您仍然应该承诺它。

function myCall(item){
    var d = $q.defer();
    myCallFunction(item,function(r){ d.resolve({val:r,item:item});}
                       ,function(r){ d.reject(r);});
    return d.promise;
}

注意,我们是在异步函数完成之后解决延迟,而不是在它之前。

现在,我们需要实现一个“Settle”函数,它会在所有的 Promise 都完成时进行解析。这类似于$q.all,但会等待所有承诺解决而不履行。

function settle(promises){
     var d = $q.defer();
     var counter = 0;
     var results = Array(promises.length);
     promises.forEach(function(p,i){ 
         p.then(function(v){ // add as fulfilled
              results[i] = {state:"fulfilled", promise : p, value: v};
         }).catch(function(r){ // add as rejected
              results[i] = {state:"rejected", promise : p, reason: r};
         }).finally(function(){  // when any promises resolved or failed
             counter++; // notify the counter
             if (counter === promises.length) {
                d.resolve(results); // resolve the deferred.
             }
         });
     });
}

这种结算功能存在于大多数 Promise 实现中,但不存在于 $q 中。我们也可以通过拒绝和$q.all 来做到这一点,但这意味着流控制的例外情况,这是一种不好的做法。

现在,我们可以settle

 settle(queue.map(myCall)).then(function(results){
     var failed = results.filter(function(r){ return r.state === "rejected"; });
     var failedItems = failed.map(function(i){ return i.value.item; });
 });

【讨论】:

    【解决方案2】:

    这是一个简洁的解决方案,可以解决非常有限的 $q 的限制,而无需使用庞大的函数/polyfill 来扩充其方法。

    特别是,

    • $q 的 promise 不包含用于查询其状态的简单机制
    • $q 有一个.all() 方法,但没有allSettled()

    我在这里使用的技巧是:

    • 在一个数组中信守承诺,并在一致的第二个数组中记录其(最终)成功
    • 解决关于成功和失败的承诺,从而允许$q.all() 表现得像缺少的$q.allSettled()
    function saveQueue() {
        //First some safety
        if(queue.saving) {
            return $q.defer().resolve(-1).promise;
        }
        queue.saving = true;
    
        var settled = [],//the sole purpose of this array is to allow $q.all() to be called. All promises place in  this array will be resolved.
            successes = [];//an array to be (sparsely) populated with `true` for every item successfully saved. This helps overcome the lack of a simple test of a $q promise's state (pending/fulfilled/rejected).
    
        queue.forEach(function(item, i) {
            var defer = $q.defer(); 
            settled[i]  = defer.promise;
            myCallFunction(item, function(response) {
                //here do awesome stuff with the response
                //`item`, if required, is in scope
                successes[i] = true;//register the promise's success
                defer.resolve();//as you would expect
            }, function(error) {
                //here do awesome stuff with the error (eg log it).
                //`item`, if required, is in scope
                defer.resolve();//here we *resolve*, not reject, thus allowing `$q.all(settled)` to reflect the settling of all promises regardless of whether they were fulfilled or rejected.
            });
        });
    
        // Once all items have been processed
        return $q.all(settled).then(function() {
            queue = queue.filter(function(val, i) {
                return !successes[i];
            });
            queue.saving = false;
            return queue.length;
        });
    }
    

    saveQueue() 将返回:

    • 如果之前的 saveQueue() 仍在进行中,则为 -1 的承诺,或者
    • 所有保存完成后的队列长度承诺。

    纯粹主义者无疑会认为这个解决方案是“反模式”(yuk!),因为需要解决关于成功和错误的承诺,但是问题的性质和 $q 的局限性鼓励我们朝这个方向发展。

    除此之外,您可能还需要一种机制来确保放入队列中的项目是唯一的。重复最好的情况是浪费,最坏的情况可能会导致错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-30
      • 1970-01-01
      • 2021-12-15
      • 2021-08-28
      • 1970-01-01
      • 1970-01-01
      • 2023-01-19
      • 2022-11-30
      相关资源
      最近更新 更多