【问题标题】:Using Promise constructor to transform each element in an array and return the element back使用 Promise 构造函数转换数组中的每个元素并返回元素
【发布时间】:2018-02-14 01:16:55
【问题描述】:

我正在使用 Geocodio,它是基于回调的地理编码。我有一个对象数组,每个对象都嵌套了一个餐厅对象。请参见下面的示例:

{
    id: 0, title: "Half off all pizzas", 
    details:"Carry out only, all-day", 
    days: ["Monday", "Tuesday"],
    restaurant: {
        name: "Papa John's", 
        addressOne: "2937 Greenville Ave", 
        city: "Dallas", 
        state: "TX", 
        zip: 75206
    } 
}

我目前正在映射对象数组,以将每个餐厅的地址传递到地理编码函数中。然后,我想向该对象添加一个名为 location 的属性,该属性将包含地理编码的结果。但是,在函数的结果可以传递给对象之前,该元素被返回。见下文:

generateCoordinates (req, res, next) {
    var newDeals = deals.map((cur, ind, arr) => {
        location = new Promise (function(resolve, reject) { 
            geocodio.get('geocode',  {q: `${cur.restaurant.addressOne}, 
                ${cur.restaurant.city}, ${cur.restaurant.state}, 
                ${cur.restaurant.zip}`}, function(err, response) {
                    if (err) {
                        reject(err)
                        throw err;
                    }
                    else {
                        var result = JSON.parse(response);
                        let obj = result.results[0].location;
                        // console.log(obj);
                        resolve(obj)
                    }
                }
            )}).then((obj) => {
                cur.location = obj;
                console.log(cur)
                return cur;
            })
        return cur;
        // console.log(newDeals)
    }) 
    res.status(200).send(newDeals)
},

我很可能在这里误用了 Promise 构造函数。 .then 中的 console.log 显示每个元素都正确添加了 location 属性,但响应和 console.log(newDeals) 将值显示为“Promise”。

如何设置数组中每个元素的位置属性?

【问题讨论】:

  • 不,你没有滥用它,但你应该返回承诺并在返回的数组上使用Promise.all
  • geocodio.get() 是否返回 Promise?如果确实如此,您也许可以避免显式的 Promise 构造。
  • 您可能不需要手动创建 Promise。如果您在节点 8+ 上,您可以使用 util.promisify 将回调样式函数提升为基于 Promise 的函数。

标签: javascript arrays node.js geocoding es6-promise


【解决方案1】:

是的,您不应该在异步回调中执行任何操作,而是调用 resolvereject。特别是你不应该throw

return cur 并不完全有效,因为它发生在 promise 被解决之前。您需要从map 回调中返回承诺,然后使用Promise.all 等待数组中的所有承诺。当最终实现时,您才能发送响应。也不要忘记在发生错误时发送适当的响应。

generateCoordinates (req, res, next) {
    var newDeals = deals.map((cur, ind, arr) =>
        new Promise((resolve, reject) => {
            geocodio.get('geocode', {
                q: `${cur.restaurant.addressOne},
                    ${cur.restaurant.city}, ${cur.restaurant.state},
                    ${cur.restaurant.zip}`
            }, (err, response) => {
                if (err) reject(err);
                else resolve(response);
            });
        }).then(response => {
            var result = JSON.parse(response);
            let obj = result.results[0].location;
            cur.location = obj;
            console.log(cur)
            return cur;
        })
    );
    Promise.all(newDeals).then(results => {
        res.status(200).send(results);
    }, err => {
        res.status(500); // or whatever
        console.error(err);
    });
}

【讨论】:

  • 仅供参考,您的代码将不起作用。您需要在地图内返回承诺,否则您会得到一个填充有 undefined 的数组
  • @KarimHKyler 我使用了一个带有简洁主体的箭头函数,它隐式地返回了承诺。注意“缺失”的大括号。
  • 哇。你们是天才。感谢您的帮助!这行得通。响应返回正确的数据。
【解决方案2】:

所以你想返回一个值数组?你几乎拥有它,但你需要等待 Promise 解决。像这样:

generateCoordinates (req, res, next) {
    var newDeals = deals.map((cur, ind, arr) => {
        return new Promise (function(resolve, reject) { 
            geocodio.get('geocode',  {q: `${cur.restaurant.addressOne}, 
            ${cur.restaurant.city}, ${cur.restaurant.state}, 
            ${cur.restaurant.zip}`}, function(err, response) {
                if (err) {
                   return reject(err);
                }
                else {
                    var result = JSON.parse(response);
                    let obj = result.results[0].location;
                    // console.log(obj);
                    resolve(obj)
                }
            }
        )}).then((obj) => {
            cur.location = obj;
            console.log(cur)
            return cur;
        });

    //newDeals is an array of promises at this point
    Promise.all(newDeals).then(results => {
      //results here is a value resolved from your promise above
      return res.status(200).send(results)
    }).catch(er r=> {
    //handle errror
    });
},

我正在做的是在这里使用 map 来创建一系列承诺。然后使用Promise.all 等待承诺完成。从map 中的then 块返回的任何内容都将作为Promise.allthen 块中的数组结束。如果您拒绝任何承诺,它会自动在Promise.all 中捕获它们

【讨论】:

  • 这行得通。最初认为它不起作用,因为我没有注意到你已经返回了新的 Promise。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-17
  • 1970-01-01
相关资源
最近更新 更多