Promise 在 forEach 循环完成之前解决
Posted
技术标签:
【中文标题】Promise 在 forEach 循环完成之前解决【英文标题】:Promise resolves before forEach loop completes 【发布时间】:2018-04-28 03:50:57 【问题描述】:我正在构建我的第一个 CRUD(库)应用程序,并且最近了解了将 Promise 作为一种避免深度嵌套回调的方法。每次服务器启动时,我都试图用一些数据为我的数据库播种,但我似乎在概念上遗漏了一些东西。
我在 bookData
数组中有四个对象,我想使用 Mongoose 对其进行迭代并保存到数据库:
function seedBooks()
return new Promise(function(resolve,reject)
bookData.forEach(function(seed)
Book.create(seed, function(err, newBook)
if(err)
reject(err);
);
);
resolve();
);
这个函数是我试图链接在一起的几个函数之一,这就是我使用 Promise 的原因。但我发现seedBooks()
可以解决 1 到 4 个样本书的创建问题,
function seedDB()
removeAllBooks()
.then(function()
return removeAllUsers();
)
.then(function()
return seedUsers();
)
.then(function()
return seedBooks();
)
.then(function()
return seedBookInstances();
);
我是否理解或使用了 promise & resolve 不正确?任何帮助表示赞赏。谢谢!
【问题讨论】:
【参考方案1】:您正在同步解决您的 Promise,就在您启动您的 forEached 请求之后,这些请求是异步的。您可以尝试以下方法:
function seedBooks()
return new Promise(function(resolve,reject)
var count = 0, length = bookData.length;
bookData.forEach(function(seed)
Book.create(seed, function(err, newBook)
if(err)
reject(err);
return;
if(count++ >= length )
resolve();
);
);
);
这里的 Promise 只有在所有异步请求都完成后才会被解析。
另一种选择是使用Promise.all。在这种方法中,您需要在循环中承诺所有请求,返回一个 Promises 数组,然后调用 Promise.all(_seedBooks()).then()
,其中 _seedBook
返回一个 Promises 数组:
function _seedBooks()
return bookData.map(function(seed)
return new Promise(function(resolve, reject)
Book.create(seed, function(err, newBook)
if(err)
reject(err);
return;
resolve(newBook);
);
);
);
Promise.all(_seedBooks())
.then(function(result) /* result is the array of newBook objects */ )
.catch(function(error) /* error is the first rejected err */ )
【讨论】:
【参考方案2】:编辑: 下面解释了为什么您的代码无法正常工作,并就如何将 non-promise 代码转换为 Promise 提供一般建议。但是,由于 Mongoose 会产生 Promise,因此您应该使用它们而不是使用 new Promise
。请参阅Olegzandr's answer。
承诺会立即解决,因为您正在立即调用resolve()
。
将非承诺转换为承诺时的经验法则是承诺非承诺代码的最小部分。在这种情况下,这意味着承诺代码以保存单个项目。如果你这样做,呼叫resolve()
的收集地点就会变得清晰:
function seedBook(seed)
return new Promise(function (resolve, reject)
Book.create(seed, function (err, newBook)
if (err) reject(err); else resolve(newBook);
);
);
function seedBooks()
return Promise.all(bookData.map(seedBook));
如果您愿意,这还有一个好处是允许您访问返回的newBook
s。
【讨论】:
【参考方案3】:如果您使用的是 Mongoose,您可以这样做:
const saveBooks = function(books)
return books.map(function(seed)
return Book.create(seed); // returns a promise
);
);
return Promise.all(saveBooks(books)).then(function()
// all books are saved
);
【讨论】:
以上是关于Promise 在 forEach 循环完成之前解决的主要内容,如果未能解决你的问题,请参考以下文章
在嵌套 forEach 中的 Promise.all 之前评估 Promise,导致 Promise.all 为空
Javascript等待Promise在返回响应之前完成for循环