node中map函数同步执行

发布于 2022-09-06 09:03:17 字数 453 浏览 10 评论 0

看到这样一句话 node中map()有一个特性:当其函数里面里面有回调它就变成异步;
我现在有一个这样的一个函数,遍历数组从mongodb中找出想要的数据。

function save(arr) {
        let newArr = [];
        arr.map((item) => {
            Model.findOne({_id: item.id}, (err, data) => {
                newArr.push(data)
                //   do something
            })
        });
        console.log(newArr)
    }

我不是知道如何将这个函数改为同步的,我想要得到一个有数据的newArr,而不是一个空数组,
请指教!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

日裸衫吸 2022-09-13 09:03:17

一日吸猫,终身吸猫。一旦异步,就不要试图在其中混入同步的逻辑,这会极大地损失性能和造成各种问题。相反,你应该把所有同步的逻辑想办法改为异步的,这是使用异步编程的最基本要求,也是对同步编程思维方式最大的一个挑战。
异步的思维方式要求你找到合适的时间点触发合适的事件。比如你的场景可以这样理解:

  1. arr中的每个元素都可以找到一个对应的数据库文档
  2. 同时对数据库发起n个请求,每个请求根据_id查询对应的记录(这就是map里面做的事情)
  3. 这些查询得到结果是有先后的,但是谁先谁后不知道
  4. 你需要的时间节点是:当所有查询都返回结果并pushnewArr之后再执行后续操作

4的要求可以抽象为:同时执行n个操作,如何在n个操作都完成后再执行下面的操作。这是异步编程里面经常遇到的问题。因为经常遇到,所以肯定也已经有解决方案了:

  1. Bluebird
  2. async

这两个类库请挑一个仔细学习,都可以解决你的问题。因为这是使用异步编程必须跨过的一关,必须要你自己理解,我在这里不给具体的答案,请先尝试自己解决。

就事论事来说你的问题,无论是从效率还是便捷性上来说,其实你都做得太复杂了(当然不排除你是为了实验目的)。从解决问题的角度来说,建议直接使用$in

function save(arr, callback) {
    let newArr = [];
    let ids = arr.map((item) => {
        return item.id
    });
    Model.find({_id: {$in: ids}}, (err, data) => {
        if (err) throw err;
        // data就是你需要的结果
        console.log(JSON.stringify(data));
        callback(err, data);
    })
}
眼波传意 2022-09-13 09:03:17

今天也遇到了同样的问题,不过跟你的场景不一样,我们其实只需要知道map函数遍历是立即执行的,即使你内部定义了async,并进行了await异步操作,但是map函数并不知道你什么时候返回完成状态的promise,所以理所当然的map函数最后返回的是一个promise对象数组。比如我遇到的场景是需要在已有的文章列表数据中,将标签字段的字符串(如; '1,2,3,5'id逗号隔开)关联标签表并获取标签名称合并到原来的文章列表数据中。这其中就需要遍历list并把标签ids取出并异步查询同时将结果合并到每一条列表数据里面。此时代码应该是这样的

    const result = articles.map(async item => {
      let tagInfo = null
      const tagIds = item.tags.split(',')
      tagInfo = await tagsModel.findAll({
        where: {
          id: {
            [Op.in]: tagIds
          }
        }
      })
      return Object.assign(item.dataValues, { tagInfo })
    })

这里可以看到的是map将每一条数据和tagInfo进行了合并,然后一起返回,由于上面说到的原因,所以这里得到的result应该是

[ Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> } ]

知道了数组里面装着的是promise对象我们要获取也很简单,不要忘了async/await也是建立在promise的基础上的,所以这里不妨利用promise.all方法解决问题

const list = await Promise.all(result).catch(err => ctx.throw(500))
ctx.success({ list, pageIndex, pageSize, total: totalRecord }, '操作成功')

这样下来便拿到了list数据,同时也将结果返回给了前端

权谋诡计 2022-09-13 09:03:17

首先,你这样用map和用forEach没什么区别。

其次,对于异步并行操作,用promise.all,不要用单纯的循环。

路弥 2022-09-13 09:03:17

你这个问题跟map没关系
这样试一下
mongoose 4 操作结果好像是promise

var save= async function (arr) {
    let newArr = [];
    for(var i=0;i<arr.length;i++){//await 不能嵌套在函数内所以只能用for循环
        await Model.findOne({_id: arr[i].id}, (err, data) => {
            newArr.push(data)
        })
    }
    console.log(newArr)
};
帝王念 2022-09-13 09:03:17

async await 写一个函数new promise你查询到的值,然后用async定义save函数,用await来获取你resolve的值,在通过push生成数组。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文