使用有序$ q.all Promises

时间:2017-05-16 15:52:45

标签: angularjs angular-promise

第二次更新

我们正在实施 BulkEdit 功能,可将异步CRUD请求发送到后端。

所以我需要的是一个动态创建的嵌套承诺

在抽象版本中,数据数组可能如下所示:

   var objArr = [
    {
      name: 'A',
      subs: [
        {
          id: 1,
          _action: 'create'
        },
        {
          id: 2,
          _action: 'create'
        },
        {
          id: 3,
          _action: 'delete'
        }
      ]
    },
    {
      name: 'B',
      subs: [
        {
          id: 4,
          _action: 'create'
        },
        {
          id: 5,
          _action: 'put'
        }
      ]
    },
    {
      name: 'C',
      subs: []
    }
  ];

我尝试说明如何按照'_action'给出的顺序为这些数据发送请求。

  1. 获取一些交易ID(见下文)
  2. 只要有事务ID,就会开始根据以下规则发送对Array中每个Object的请求:

    • 如果有任何对象,则每次发送所有“删除”请求。
    • 之后或如果没有任何'删除'请求发送所有'put' 请求是否有。
    • 在'删除'和/或'put'之后发送所有'创建'请求(如果有)。
  3. 只要完成对Object的所有请求,就按对象执行操作。

  4. 完成所有对象后,请关闭交易。
  5. Illustrative Request Flow

    如何创建此动态嵌套/非嵌套保证链?

    更新后的代码 现在包含Promise Creation

    当调用下面的函数时,首先TransactionService获取一个事务id,该事务id需要随每个请求一起发送。一切顺利后,交易将被关闭。

    我当前的问题是Promises没有以正确的顺序解析(当OPTIONS预检请求似乎是这样)并且这个例子创建了Promises,即使它们不是必需的(例如上面例子中的Object'C')

    function startIt(objArr) {
    
      TransactionService.getTransaction().then(function (transaction) {
    
        var promiseArray = MyService.submit(objArr, transaction.id);
    
        $q.all(promiseArray).then(function () {
    
          Transactions.closeTransaction(transaction.id, function () {}).then(function () {
    
          });
        };
      });
    }
    

    这是'submit'的功能:

     function submit(objArr, transactionId) {
    
      var promises = objArr.map(function (obj) {
        return submitWithTransId(transactionId, obj)
          .then(function (response) {
             // Object done
          });
      });
    
      return promises;
    }
    

    这个函数实际上是在创建Promises:

    function submitWithTransId(transactionId, obj) {
    
      var promisesDelete = [];
      var promisesUpdate = [];
      var promisesCreate = [];
    
      angular.forEach(obj['subs'], function (sub) {
    
          switch (sub._action) {
            case 'delete':
              promisesDelete.push(createPromise(sub, bulktransactionId));
              break;
            case 'put':
              promisesUpdate.push(createPromise(sub, bulktransactionId));
              break;
            case 'create':
              promisesCreate.push(createPromise(sub, bulktransactionId));
              break;
        }
      });
    
      var chainedPromises = $q.all(promisesDelete).then(function (deleteResponse) {
          return $q.all(promisesUpdate).then(function (updateResponse) {
            return $q.all(promisesCreate).then(function (createResponse) {
            });
          });
        });
    
      return chainedPromises;
    

    这是我的createPromise功能:

    /** only simplified handling create case **/
    
    function createPromise(sub, bulktransactionId) {
    
    var queryParams = {};
    if (bulktransactionId !== undefined && bulktransactionId !== null) {
       queryParams.transaction_id = bulktransactionId;
    }
    
    var promise = MyResourceService.create(queryParams, sub).$promise;
    promise.then(function (newSub) {
        // do something with the new/updated/deleted sub, especially update view model
     });
     return promise;
    }
    
    /** MyResourceService **/
    return $resource(ENV.apiEndpoint + 'api/v1/subs/:_id',
     {
      _id: '@id'
     }, {
      create: {
       method: 'POST'
      }
     }
    );
    

3 个答案:

答案 0 :(得分:3)

您可以查看以下解决方案。目标是为您提供某种结构。请注意,您必须修改您的使用。

 var objArr = [{
        name: 'A',
        subs: [{
                id: 1,
                _action: 'create'
            },
            {
                id: 2,
                _action: 'create'
            },
            {
                id: 3,
                _action: 'delete'
            }
        ]
    },
    {
        name: 'B',
        subs: [{
                id: 4,
                _action: 'create'
            },
            {
                id: 5,
                _action: 'put'
            }
        ]
    },
    {
        name: 'C',
        subs: []
    }
];



var promises = objArr.map(function(obj) {
    return firstLevelPromise(obj)
        .then(function(response) {
            console.log(response); // promise for each object
            return response;
        });
});

$q.all(promises)
    .then(function(response) {
        console.log(response); // completion - close transaction
    });


function firstLevelPromise(obj) {
    var deletePromises = [];
    var putPromies = [];
    var insertPromies = [];

    obj.subs.forEach(function(sub) { // Preparing promises array for delete, put and insert
        if (sub._action === "delete") {
            deletePromises.push(deletePromise(sub));
        } else if (sub._action === "put") {
            putPromies.push(putPromise(sub));
        } else {
            insertPromies.push(insertPromise(sub));
        }
    });

    return $q.all(deletePromises) // executing delete promises
    .then(function(deleteResponse) {
        console.log("deleteExecuted: " + obj.name);
        return $q.all(putPromies) // on completion of delete, execute put promies
            .then(function(putResponse) {
                console.log("putExecuted: " + obj.name);
                return $q.all(insertPromies) // on completion of put, execute insert promises
                    .then(function(insertResponse) {
                        console.log("insertExecuted: " + obj.name);
                        return "object promise completed: " + obj.name; // on completion, return
                    });
            });
    });
}

function deletePromise(task) {
    return $q.resolve(task); // write your delete code here
}

function putPromise(task) {
    return $q.resolve(task); // write your put code here
}

function insertPromise(task) {
    return $q.resolve(task); // write your insert code here
}

请注意,上面的代码将执行以下操作

  • 准备一组承诺,其中objectArray中的每个对象都有一个承诺
  • 每个承诺都有承诺链,即删除承诺,然后是承诺,最后是插入承诺,即它确保每个对象执行删除任务,然后在完成时执行put任务,然后在完成时执行插入任务。

以下是plunker

$q和文档

<强>更新

问题在于createPromise功能。将代码更新为以下内容。问题是你在返回之前调用了then,因此,很可能你正在返回一个已解决的承诺,如$q.resolve()。在这种情况下,可能会在createdelete之前解决put请求,并在put之前解析delete次来电。因此,您应该从此处返回承诺,并在$q.all块中执行后期操作。

function createPromise(sub, bulktransactionId) {

    var queryParams = {};
    if (bulktransactionId !== undefined && bulktransactionId !== null) {
       queryParams.transaction_id = bulktransactionId;
    }
    return MyResourceService.create(queryParams, sub).$promise;
}

答案 1 :(得分:1)

试试这个 - 诀窍是积累承诺(这就是我正在使用的reduce。希望它有所帮助。

// reversed the order => delete actions first; otherwise you may have to do extra logic may be needed
var objArr = [ 
   { name: 'A', 
     subs: [
        { id: null,
          _action: 'delete'
        }, 
        { id: 2,
          _action: 'create']
        }
   },
   { name: 'B',
     subs: [
        { id: 3.
          _action: 'create'
        }
     ]
 ];

 Promise.all(objArr.map(obj => {
   return obj.subs.reduce((accumulatedPromisedSub, sub) => {
     return accumulatedPromisedSub.then(_ => yourRequestCallHere(sub) )
   },
   Promise.resolve(true) // you could also do your delete here if you like
   )
 }))

// OR going sort order agnostic:

 Promise.all(objArr.map(obj => {
   const deleteAction = obj.subs.find(sub => sub._action === 'delete');

   return obj.subs.reduce((accumulatedPromisedSub, sub) => {
     if (sub._action === 'delete') return accumulatedPromisedSub;
     return accumulatedPromisedSub.then(_ => yourRequestCallHere(sub) )
   },
   deleteAction ? yourRequestCall(deleteAction) : Promise.resolve(true)
   )
 }))

答案 2 :(得分:1)

我很高兴和感恩,我找到了。

根据我的理解,我的代码中有两个主要问题。

由于$resource没有提供(请原谅我,专业人士!)真实 $promise即使我使用.get().$promise之类的东西我也必须

  1. 使用bind createPromise 功能映射到我的数组
  2. 仅在返回$q.all承诺之前直接进行映射。
  3. 在任何其他情况下,$resource似乎立即用空对象填充其承诺,$q.all不再起作用,因为承诺看起来像是在解决它们。

    特别感谢@nikhil,他支持我找到这个丑陋复杂的东西并赢得了赏金。

    这是工作片段:

    function submitWithTransId(transactionId, obj) {
    
     var arrayDelete = [];
     var arrayUpdate = [];
     var arrayCreate = [];
    
     angular.forEach(obj['subs'], function (sub) {
    
       switch (sub._action) {
         case 'delete':
           arrayDelete.push(sub);
           break;
         case 'update':
           arrayUpdate.push(sub);
           break;
         case 'create':
           arrayCreate.push(sub);
           break;
        }
     });
    
     var promisesDelete = flattenArray(arrayDelete.map(createPromise.bind(undefined, obj, transactionId)));
    
     return $q.all(promisesDelete).then(function (deleteResponse) {
    
        console.log('Delete Promises ' + obj.name + ' resolved');
    
        var promisesUpdate = flattenArray(arrayUpdate.map(createPromise.bind(undefined, obj, transactionId)));
    
        return $q.all(promisesUpdate).then(function (updateResponse) {
          var promisesCreate = flattenArray(arrayCreate.map(createPromise.bind(undefined, obj, transactionId)));
    
          console.log('Update Promises ' + obj.name + ' resolved');
    
          return $q.all(promisesCreate).then(function (createResponse) {
    
            console.log('Create Promises ' + obj.name + ' resolved');
    
          });
        });
      }).catch(function (error) {
        console.log('Catched an error: ');
        console.log(error);
      });
    

    });