2014-01-29 121 views
1

我有一個函數createOne()應該嘗試找到一個MongoDB的文檔或創建它,如果它不能被發現。在這兩種方式,一種承諾應返回,並承諾與doc解決:承諾返回值和築巢

var createOne = function(category) { 
    var self = this; 

    return this.findOne({ slug: category.slug }).exec() 
     .then(function(doc) { 
      if (!doc) {      
       return self.create(category); // wait to resolve until document created. 
      } 
     }); 
}; 

兩個findOne().exec()create()(應該)返回一個承諾。

我嘗試了很多不同的方式,例如使用Q.fcall,用Q.defer()和其他方法手動創建一個,但是解析值丟失,或者創建了第二個{ slug: 'foo' },即使第一個已經存在(?)。

以下是我調用代碼:

var data = [ 
    { slug: 'foo' }, 
    { slug: 'bar' }, 
    { slug: 'foo' } // <- shouldn't be created because of first 'foo'. 
]; 

Q.fcall(function() { 
    // [...] 
}).then(function() { 
    var promises = data.map(function(category) { 
     return createOne(category); // <- calls createOne(). 
    }); 
    return Q.all(promises); 
}).then(function(categories) { 
    console.log(categories); 
}).done(); 

我怎樣組織createOne()使console.log(categories)返回文檔,無論是發現還是第一次創建?

編輯:當集合爲空時,只應創建兩個文檔。 { slug: 'foo' }只有一次。

+0

應該不是''如果有一個返回'else'條款'doc'直接? – greim

+0

a)那只是僞代碼還是你的'.exec()'調用已經返回一個promise(帶'.then()'方法)? b)貓鼬是如何發現沒有文件被發現的,它是錯誤還是通過'null'? – Bergi

+1

是的,沒錯,謝謝!返回值現在確定:) 我認爲第二個'{slug:'foo'}'被創建是因爲我調用了我的'createOne()'rapid-fire樣式,而不是等待異步查找/創建調用完成。那可能嗎? –

回答

1

看來我已經找到了如何按順序調用異步函數和撰寫他們所有的諾言(後來經解析的值)的最後一個數組。

我的解決方案是基於一個偉大的array.reduce的例子,我在Github上一個comment from kriskowal發現。

var data = [ 
    'Element 1', 'Element 2', 'Element 3' 
]; 

function asyncMock(element) { 
    console.log('Handle', element); 
    return Q.delay(element + ' handled.', 500); 
} 

var promise = data.reduce(function (results, element) { 
    return results.then(function (results) { 
     return asyncMock(element).then(function(result) { 
      results.push(result); 
      return results; 
     }); 
    }); 
}, Q([])); 

promise.then(function(elements) { 
    console.log('Finished:', elements); 
}).done(); 

你可以找到一個工作演示在這裏:(!DOC)http://jsfiddle.net/Lnvu4/