蓝鸟地图早归
Bluebird map returning early
我正在使用 Bluebird.map
但由于某种原因它会提前返回我的对象数组:
function returnInvoicePrice(items) {
return Bluebird.map(items, function(item) {
db.collection('products').find({
product_code : item.product_code
}).toArray(function(err, product) {
var invoice_price = product[0].invoice_price;
console.log('--product--');
console.log(product);
return product;
});
}).then(function(items) {
console.log('--items w/ invoice price--'); // this prints BEFORE my console.log(product) for some reason...
console.log(items);
}).catch(function(error) {
console.log('--bluebird error in returning price for each item--');
console.log(error);
});
}
returnInvoicePrice(items);
有谁知道我可能做错了什么?
我在这里看到三个主要错误:
- Bluebirds
.map()
需要一个 return 承诺的回调。
- 您的
.then()
句柄正在执行 console.log(items)
必须 return items
以便结果承诺仍然具有 items
作为解析值。
- 当您调用
returnInvoicePrice()
时,您必须使用 .then()
来检索它的值。该函数 return 是一个 promise,它是监视 .map()
中所有其他 promise 的主要 promise。
您传递给 Bluebird.map()
的函数需要 return 一个承诺 在异步操作完成时解决。如果它 return 什么都没有或 return 是一个普通值,那么 Bluebird 不能等到异步操作完成,因此 return 会比你想要的更快。
如果您的版本还不支持 promises,可以使用诸如 mongodb-promise 之类的软件包为您提供 mongodb 的 promise 接口,或者您可以使用 Bluebird 的 .promisify()
或.promisifyAll()
在 mongodb 中承诺接口。
node 的最新版本 mongodb 在其所有异步接口中支持 promise,因此您可以直接 return 它提供的 promise。
从 this doc for the latest mongodb 看来,xxx.find().toArray()
return 是一个承诺,因此您可以 return 像这样直接从 .map()
回调中 return 这个承诺(以及修正其他错误):
function returnInvoicePrice(items) {
return Bluebird.map(items, function(item) {
// return promise here
return db.collection('products').find({
product_code : item.product_code
}).toArray(function(err, product) {
var invoice_price = product[0].invoice_price;
console.log('--product--');
console.log(product);
return product;
});
}).then(function(items) {
console.log('--items w/ invoice price--'); // this prints BEFORE my console.log(product) for some reason...
console.log(items);
// also have to return items here
return items;
}).catch(function(error) {
console.log('--bluebird error in returning price for each item--');
console.log(error);
});
}
// have to use `.then()` on the returned promise here
returnInvoicePrice(items).then(function(items) {
// can access items here
});
如果您运行此代码:
var Bluebird = require('bluebird');
function returnInvoicePrice(items) {
return Bluebird.map(items, function(item) {
console.log(item);
return item;
}).then(function(items) {
console.log('--items w/ invoice price--'); // this prints BEFORE my console.log(product) for some reason...
console.log(items);
}).catch(function(error) {
console.log('--bluebird error in returning price for each item--');
console.log(error);
});
}
returnInvoicePrice([1,2,3]);
您会看到您期望的顺序。不同之处在于,正如 jfriend00 所暗示的那样 db.collection.find(...) 是异步的,在未来的某个时间点执行。因此,您需要将该部分包装在 Promise 中:
return new Promise(function (resolve, reject) {
db.collection.find({
:
resolve(); // this will signal Bluebird to execute the mapper function
});
}
这样,Bluebird 就知道在继续之前等待响应。
您可以通过 运行 模拟异步进程的代码看到这一点:
var Bluebird = require('bluebird');
function returnInvoicePrice(items) {
return Bluebird.map(items, function(item) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log(item);
resolve(item);
}, 500);
});
}).then(function(items) {
console.log('--items w/ invoice price--'); // this prints BEFORE my console.log(product) for some reason...
console.log(items);
}).catch(function(error) {
console.log('--bluebird error in returning price for each item--');
console.log(error);
});
}
returnInvoicePrice([1,2,3]);
我正在使用 Bluebird.map
但由于某种原因它会提前返回我的对象数组:
function returnInvoicePrice(items) {
return Bluebird.map(items, function(item) {
db.collection('products').find({
product_code : item.product_code
}).toArray(function(err, product) {
var invoice_price = product[0].invoice_price;
console.log('--product--');
console.log(product);
return product;
});
}).then(function(items) {
console.log('--items w/ invoice price--'); // this prints BEFORE my console.log(product) for some reason...
console.log(items);
}).catch(function(error) {
console.log('--bluebird error in returning price for each item--');
console.log(error);
});
}
returnInvoicePrice(items);
有谁知道我可能做错了什么?
我在这里看到三个主要错误:
- Bluebirds
.map()
需要一个 return 承诺的回调。 - 您的
.then()
句柄正在执行console.log(items)
必须return items
以便结果承诺仍然具有items
作为解析值。 - 当您调用
returnInvoicePrice()
时,您必须使用.then()
来检索它的值。该函数 return 是一个 promise,它是监视.map()
中所有其他 promise 的主要 promise。
您传递给 Bluebird.map()
的函数需要 return 一个承诺 在异步操作完成时解决。如果它 return 什么都没有或 return 是一个普通值,那么 Bluebird 不能等到异步操作完成,因此 return 会比你想要的更快。
如果您的版本还不支持 promises,可以使用诸如 mongodb-promise 之类的软件包为您提供 mongodb 的 promise 接口,或者您可以使用 Bluebird 的 .promisify()
或.promisifyAll()
在 mongodb 中承诺接口。
node 的最新版本 mongodb 在其所有异步接口中支持 promise,因此您可以直接 return 它提供的 promise。
从 this doc for the latest mongodb 看来,xxx.find().toArray()
return 是一个承诺,因此您可以 return 像这样直接从 .map()
回调中 return 这个承诺(以及修正其他错误):
function returnInvoicePrice(items) {
return Bluebird.map(items, function(item) {
// return promise here
return db.collection('products').find({
product_code : item.product_code
}).toArray(function(err, product) {
var invoice_price = product[0].invoice_price;
console.log('--product--');
console.log(product);
return product;
});
}).then(function(items) {
console.log('--items w/ invoice price--'); // this prints BEFORE my console.log(product) for some reason...
console.log(items);
// also have to return items here
return items;
}).catch(function(error) {
console.log('--bluebird error in returning price for each item--');
console.log(error);
});
}
// have to use `.then()` on the returned promise here
returnInvoicePrice(items).then(function(items) {
// can access items here
});
如果您运行此代码:
var Bluebird = require('bluebird');
function returnInvoicePrice(items) {
return Bluebird.map(items, function(item) {
console.log(item);
return item;
}).then(function(items) {
console.log('--items w/ invoice price--'); // this prints BEFORE my console.log(product) for some reason...
console.log(items);
}).catch(function(error) {
console.log('--bluebird error in returning price for each item--');
console.log(error);
});
}
returnInvoicePrice([1,2,3]);
您会看到您期望的顺序。不同之处在于,正如 jfriend00 所暗示的那样 db.collection.find(...) 是异步的,在未来的某个时间点执行。因此,您需要将该部分包装在 Promise 中:
return new Promise(function (resolve, reject) {
db.collection.find({
:
resolve(); // this will signal Bluebird to execute the mapper function
});
}
这样,Bluebird 就知道在继续之前等待响应。
您可以通过 运行 模拟异步进程的代码看到这一点:
var Bluebird = require('bluebird');
function returnInvoicePrice(items) {
return Bluebird.map(items, function(item) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log(item);
resolve(item);
}, 500);
});
}).then(function(items) {
console.log('--items w/ invoice price--'); // this prints BEFORE my console.log(product) for some reason...
console.log(items);
}).catch(function(error) {
console.log('--bluebird error in returning price for each item--');
console.log(error);
});
}
returnInvoicePrice([1,2,3]);