当前位置: 首页 > 面试题库 >

使用外部API调用和findOneAndUpdate循环结果

韩靖琪
2023-03-14
问题内容

我正在尝试编写一个程序,该程序使用mongoose从mongo数据库中获取文档,并使用API​​对其进行处理,然后使用处理结果编辑数据库中的每个文档。我的问题是我有问题,因为我不完全了解nodejs和异步。这是我的代码:

Model.find(function (err, tweets) {
    if (err) return err;
    for (var i = 0; i < tweets.length; i++) {
        console.log(tweets[i].tweet);
        api.petition(tweets[i].tweet)
            .then(function(res) {
                TweetModel.findOneAndUpdate({_id: tweets[i]._id}, {result: res}, function (err, tweetFound) {
                    if (err) throw err;
                    console.log(tweetFound);
                });
            })
            .catch(function(err) {
                console.log(err);
            })
    }
})

问题在于,在findOneAndUpdate中,tweets是未定义的,因此无法找到该ID。有什么办法吗?谢谢


问题答案:

您真正缺少的核心是Mongoose API方法也使用“ Promises”,但是您似乎只是从文档或使用回调的旧示例中复制代码。解决方案是仅转换为使用Promises。

处理承诺

Model.find({},{ _id: 1, tweet: 1}).then(tweets => 
  Promise.all(
    tweets.map(({ _id, tweet }) => 
      api.petition(tweet).then(result =>   
       TweetModel.findOneAndUpdate({ _id }, { result }, { new: true })
         .then( updated => { console.log(updated); return updated })
      )
    )
  )
)
.then( updatedDocs => {
  // do something with array of updated documents
})
.catch(e => console.error(e))

除了从回调进行常规转换外,主要更改是Promise.all()用于Array.map()根据来自.find()而不是for循环的结果来解析正在处理的输出。实际上,这是您尝试中遇到的最大问题之一,因为for不能实际控制异步函数何时解析。另一个问题是“混合回调”,但这就是我们通常仅使用Promises解决的问题。

在中,Array.map()我们Promise从API调用返回,链接到findOneAndUpdate()实际上正在更新文档的。我们还用于new: true实际返回修改后的文档。

Promise.all()允许“承诺数组”解析并返回结果数组。这些您视为updatedDocs。这里的另一个优点是内部方法将以“并行”方式而不是串行方式触发。尽管这需要更多资源,但这通常意味着更快的分辨率。

还要注意,我们使用“的投影” { _id: 1, tweet: 1 }仅从结果中返回这两个字段,Model.find()因为它们是其余调用中唯一使用的字段。当您不使用其他值时,这样做可以节省返回每个结果的整个文档的时间。

您可以简单地Promise从中返回findOneAndUpdate(),但我只是添加中,console.log()这样您就可以看到输出正在触发。

正常的生产用途应该没有它:

Model.find({},{ _id: 1, tweet: 1}).then(tweets => 
  Promise.all(
    tweets.map(({ _id, tweet }) => 
      api.petition(tweet).then(result =>   
       TweetModel.findOneAndUpdate({ _id }, { result }, { new: true })
      )
    )
  )
)
.then( updatedDocs => {
  // do something with array of updated documents
})
.catch(e => console.error(e))

另一种“调整”可能是使用“蓝鸟”的实施Promise.map(),这两者结合共同Array.map()Promise(S)与控制能力运行并行调用的“并发”执行:

const Promise = require("bluebird");

Model.find({},{ _id: 1, tweet: 1}).then(tweets => 
  Promise.map(tweets, ({ _id, tweet }) => 
    api.petition(tweet).then(result =>   
      TweetModel.findOneAndUpdate({ _id }, { result }, { new: true })
    ),
    { concurrency: 5 }
  )
)
.then( updatedDocs => {
  // do something with array of updated documents
})
.catch(e => console.error(e))

“并行”的替代方案将按顺序执行。如果太多的结果导致太多的API调用和写回html" target="_blank">数据库的调用,则可以考虑以下方法:

Model.find({},{ _id: 1, tweet: 1}).then(tweets => {
  let updatedDocs = [];
  return tweets.reduce((o,{ _id, tweet }) => 
    o.then(() => api.petition(tweet))
      .then(result => TweetModel.findByIdAndUpdate(_id, { result }, { new: true })
      .then(updated => updatedDocs.push(updated))
    ,Promise.resolve()
  ).then(() => updatedDocs);
})
.then( updatedDocs => {
  // do something with array of updated documents
})
.catch(e => console.error(e))

在这里,我们可以Array.reduce()将“承诺”“链接”在一起,从而使它们能够按顺序解决。请注意,结果数组将保留在范围内,并与最后.then()添加到连接链末尾的交换掉,因为您需要这种技术来“收集”来自在该“链”中不同点处解决的Promises的结果。

异步/等待

在从NodeJS
V8.x开始的现代环境中(实际上是LTS的当前版本,并且已经存在了一段时间),您实际上已经支持async/await。这使您可以更自然地编写自己的流程

try {
  let tweets = await Model.find({},{ _id: 1, tweet: 1});

  let updatedDocs = await Promise.all(
    tweets.map(({ _id, tweet }) => 
      api.petition(tweet).then(result =>   
        TweetModel.findByIdAndUpdate(_id, { result }, { new: true })
      )
    )
  );

  // Do something with results
} catch(e) {
  console.error(e);
}

如果存在资源问题,甚至可以按顺序处理:

try {
  let cursor = Model.collection.find().project({ _id: 1, tweet: 1 });

  while ( await cursor.hasNext() ) {
    let { _id, tweet } = await cursor.next();
    let result = await api.petition(tweet);
    let updated = await TweetModel.findByIdAndUpdate(_id, { result },{ new: true });
    // do something with updated document
  }

} catch(e) {
  console.error(e)
}

还需要注意的是,findByIdAndUpdate()它也可以用作与的匹配,_id因此您不需要整个查询文档作为第一个参数。

Boulder

最后一点,如果您实际上根本不需要更新的文档作为响应,那么这bulkWrite()是更好的选择,它允许写入操作通常在单个请求中在服务器上进行处理:

Model.find({},{ _id: 1, tweet: 1}).then(tweets => 
  Promise.all(
    tweets.map(({ _id, tweet }) => api.petition(tweet).then(result => ({ _id, result }))
  )
).then( results =>
  Tweetmodel.bulkWrite(
    results.map(({ _id, result }) => 
      ({ updateOne: { filter: { _id }, update: { $set: { result } } } })
    )
  )
)
.catch(e => console.error(e))

或通过async/await语法:

try {
  let tweets = await Model.find({},{ _id: 1, tweet: 1});

  let writeResult = await Tweetmodel.bulkWrite(
    (await Promise.all(
      tweets.map(({ _id, tweet }) => api.petition(tweet).then(result => ({ _id, result }))
    )).map(({ _id, result }) =>
      ({ updateOne: { filter: { _id }, update: { $set: { result } } } })
    )
  );
} catch(e) {
  console.error(e);
}

上面显示的所有组合几乎都可以更改为该组合,因为该bulkWrite()方法采用指令的“数组”,因此您可以从上述每个方法中经过处理的API调用构造该数组。



 类似资料:
  • 问题内容: 我希望打破PHP中的外部for / foreach循环。 可以在ActionScript中完成,如下所示: PHP等效项是什么? 问题答案: 对于2个嵌套循环: http://php.net/manual/en/control- structures.break.php

  • 问题内容: 我有一条路线如下: 我正在尝试对Giant Bomb API进行API调用,以获取有关魔兽世界的所有数据。 问题是,路线刚刚加载;它什么也没做,也没有超时,只是连续加载。 我不知道我在做什么错,但是话虽这么说……我也不知道什么是对的。我在努力学习。 任何帮助都会很棒。 谢谢 问题答案: 您需要获取从中获取的数据,并将其作为对原始Web服务器请求的响应发送回去。因为您从未发送任何对原始请

  • 我有网址列表。我正在尝试从这些api中使用jsondata。下面是我尝试过的代码,但我得到了Mono.flatMapMany- 如果我使用. block(),我将为这些api获取jsonData,但问题是当我使用. block()时它变得同步。我希望它是异步的。有人能帮我吗? 提前谢谢。

  • 我试图在任意年份的经济衰退中计算复利。以下promise函数计算可变投资组合的发展。 编辑的清晰度: const是一个从1到20循环一年的数组(是静态的) 三元()在每次运行的不同年份触发,导致不同年份的

  • 我假设有很多情况需要调用基于值列表的外部RESTful服务。 现在对于这些productId中的每一个,我想并行调用一个外部endpoint。大致如下: 你会如何转换这个 到 当所有通话成功完成时。 一个人如何使用可完成的未来来实现这一点? 我想我要问的是,如何等待所有调用完成,然后以集合的形式获得结果。

  • 下面是index.js中的方法 在日志中会出现文本负载:“结果是[object]”。当我浏览url时,它会给出正确的响应。