以递归方式执行promises nodejs

时间:2015-08-20 12:44:46

标签: node.js recursion promise xml-rpc q

以下函数通过xmlrpc

在我的服务器上创建新文件夹
var createFolder = function(folder_name) {
  var defer = Q.defer();
  client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) {
    if (err) {
      if (err.responseString && err.responseString.match('already exist')) {
        //call the same function recursively with folder_name+Math.round(Math.random()*100)
      } else {
        defer.reject(err);
      }
    } else {
      defer.resolve(folder_name);
    }
  });
  return defer.promise;
}

这些功能可以成功创建一个新文件夹 但是,如果文件夹已经存在,我想以新的文件夹名称再次触发此函数,然后在promise中返回它,这样无论何时调用此函数,它都将返回文件夹名称无关紧要执行了多少次

类似

createFolder('directory').then(function(resp){
 console.log(resp);// may return directory || directory1 .... etc
});

**编辑** 所以我通过传递延迟对象来实现这一点 让我知道是否有更优雅的方法来实现这个

var createFolder = function(folder_name,defer) {
  defer =defer ||  Q.defer();
  client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) {
    if (err) {
      if (err.responseString && err.responseString.match('already exist')) {
        return createFolder(folder_name+Math.round(Math.random()*100,defer)
      } else {
        defer.reject(err);
      }
    } else {
      defer.resolve(folder_name);
    }
  });
  return defer.promise;
}

2 个答案:

答案 0 :(得分:4)

永远不要在普通(非承诺)回调中做任何逻辑。在最低级别实现承诺:

var defer = Q.defer();
client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) {
  if (err) defer.reject(err);
  else defer.resolve(folder_name);
});
return defer.promise;

或者更简单Q.ninvoke

return Q.ninvoke(client, 'methodCall', 'create_folder', [sessionID, folder_name]);

现在我们可以开始实现递归了。使用then回调非常简单,您可以从中回复另一个承诺。在你的情况下:

function createFolder(folder_name) {
  return Q.ninvoke(client, 'methodCall', 'create_folder', [sessionID, folder_name])
    .catch(function(err) {
      if (err.responseString && err.responseString.match('already exist')) {
        return createFolder(folder_name+Math.floor(Math.random()*100));
      } else {
        throw err;
      }
    });
}

答案 1 :(得分:1)

这是解决问题的糟糕简单方法:

-(void)doNewLayout 
{
 id<UICollectionViewDelegateJSPintLayout> delegate = (id<UICollectionViewDelegateJSPintLayout>)self.collectionView.delegate;

// get column width from delegate.  If the method isn't implemented fall back to our property
    NSUInteger columnWidth = self.columnWidth;
    if(delegate && [delegate respondsToSelector:@selector(columnWidthForCollectionView:layout:)])
    {
        columnWidth = [delegate columnWidthForCollectionView:self.collectionView
                                                      layout:self];
    }

    // find out how many cells there are
    NSUInteger cellCount = [self.collectionView numberOfItemsInSection:0];

    // get max number of columns from the delegate.  If the method isn't implemented, fall back to our property
    NSUInteger maximumNumberOfColumns = self.numberOfColumns;
    if(delegate && [delegate respondsToSelector:@selector(maximumNumberOfColumnsForCollectionView:layout:)]){
        maximumNumberOfColumns = [delegate maximumNumberOfColumnsForCollectionView:self.collectionView layout:self];
    }

    // build an array of all the cell heights.
    NSMutableArray* cellHeights = [NSMutableArray arrayWithCapacity:cellCount];
    for(NSUInteger cellIndex = 0; cellIndex < cellCount; ++cellIndex)
    {
        CGFloat itemHeight = self.itemHeight;  // set default item size, then optionally override it
        if(delegate && [delegate respondsToSelector:@selector(collectionView:layout:heightForItemAtIndexPath:)])
        {
            itemHeight = [delegate collectionView:self.collectionView
                                           layout:self
                         heightForItemAtIndexPath:[NSIndexPath indexPathForItem:cellIndex
                                                                      inSection:0]];
        }

        cellHeights[cellIndex] = @(itemHeight);
    }

    // now build the array of layout attributes
    self.pendingLayoutAttributes = [NSMutableArray arrayWithCapacity:cellCount];

    // will need an array of column heights
    CGFloat* columnHeights = calloc(maximumNumberOfColumns,sizeof(CGFloat));  // calloc() initializes to zero.
    CGFloat contentHeight = 0.0;
    CGFloat contentWidth = 0.0;
    for(NSUInteger cellIndex = 0; cellIndex < cellCount; ++cellIndex)
    {
        CGFloat itemHeight = [cellHeights[cellIndex] floatValue];

        // find shortest column
        NSUInteger useColumn = 0;
        CGFloat shortestHeight = DBL_MAX;
        for(NSUInteger col = 0; col < maximumNumberOfColumns; ++col)
        {
            if(columnHeights[col] < shortestHeight)
            {
                useColumn = col;
                shortestHeight = columnHeights[col];
            }
        }

        NSIndexPath* indexPath = [NSIndexPath indexPathForItem:cellIndex
                                                     inSection:0];
        UICollectionViewLayoutAttributes* layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

        layoutAttributes.size = CGSizeMake(columnWidth,itemHeight);
        layoutAttributes.center = CGPointMake((useColumn * (columnWidth + self.interitemSpacing)) + (columnWidth / 2.0),columnHeights[useColumn] + (itemHeight / 2.0));
        self.pendingLayoutAttributes[cellIndex] = layoutAttributes;
        columnHeights[useColumn] += itemHeight;
        if(columnHeights[useColumn] > contentHeight)
            contentHeight = columnHeights[useColumn];
        CGFloat rightEdge = (useColumn * (columnWidth + self.interitemSpacing)) + columnWidth;
        if(rightEdge > contentWidth)
            contentWidth = rightEdge;
        columnHeights[useColumn] += self.lineSpacing;
    }
    self.contentSize = CGSizeMake(contentWidth,contentHeight+100);

    free(columnHeights);
}

但是,var createFolder = function(folder_name) { var defer = Q.defer(); client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) { if (err) { if (err.responseString && err.responseString.match('already exist')) { //call the same function recursively with folder_name+Math.round(Math.random()*100) defer.resolve(createFolder(folder_name+Math.round(Math.random()*100))); } else { defer.reject(err); } } else { defer.resolve(folder_name); } }); return defer.promise; } 被视为不良做法。以下是very nice article about promises

你应该赞成这样的事情:

defer

编辑:正如@Bergi所指出的那样,这仍然不正确且难以调试。 var createFolder = function(folder_name) { return Q.Promise(function(resolve, reject){ client.methodCall('create_folder', [sessionID, folder_name], function(err, resp) { if (err) { if (err.responseString && err.responseString.match('already exist')) { //call the same function recursively with folder_name+Math.round(Math.random()*100) resolve(createFolder(folder_name+Math.round(Math.random()*100))); } else { reject(err); } } else { resolve(folder_name); } }); }); } 回调引发的任何潜在错误都不会实际拒绝承诺和意愿很可能被吞下(即使这个回调看起来很容易出错,它可能会发展)。请参阅his answer以获得更好的方法。

另请参阅the official Q doc here