蓝鸟地图早归

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);

有谁知道我可能做错了什么?

我在这里看到三个主要错误:

  1. Bluebirds .map() 需要一个 return 承诺的回调。
  2. 您的 .then() 句柄正在执行 console.log(items) 必须 return items 以便结果承诺仍然具有 items 作为解析值。
  3. 当您调用 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]);