【问题标题】:converting promiseAll to gradual promises resolve(every 3promises for example) does not work将承诺全部转换为逐步承诺解决(例如每 3 个承诺)不起作用
【发布时间】:2020-04-15 15:59:56
【问题描述】:

我有一个承诺列表,目前我正在使用 promiseAll 来解决它们

这是我现在的代码:

   const pageFutures = myQuery.pages.map(async (pageNumber: number) => {
      const urlObject: any = await this._service.getResultURL(searchRecord.details.id, authorization, pageNumber);
      if (!urlObject.url) {
        // throw error
      }
      const data = await rp.get({
        gzip: true,
        headers: {
          "Accept-Encoding": "gzip,deflate",
        },
        json: true,
        uri: `${urlObject.url}`,
      })

      const objects = data.objects.filter((object: any) => object.type === "observed-data" && object.created);
      return new Promise((resolve, reject) => {
        this._resultsDatastore.bulkInsert(
          databaseName,
          objects
        ).then(succ => {
          resolve(succ)
        }, err => {
          reject(err)
        })
      })
    })
    const all: any = await Promise.all(pageFutures).catch(e => {
       console.log(e)
     }) 

所以正如你在这里看到的,我使用 promise all 并且它有效:

const all: any = await Promise.all(pageFutures).catch(e => {
   console.log(e)
 }) 

但是我注意到它会影响数据库性能,所以我决定一次解决其中的 3 个问题。 为此,我正在考虑不同的方式,例如 cwait、异步池或编写我自己的迭代器 但我对如何做到这一点感到困惑?

例如当我使用 cwait 时:

let promiseQueue = new TaskQueue(Promise,3);
const all=new Promise.map(pageFutures, promiseQueue.wrap(()=>{}));

我不知道在包装内传递什么,所以我现在通过 ()=>{} 加上我得到了

Property 'map' does not exist on type 'PromiseConstructor'. 

因此,无论我如何让它工作(我自己的迭代器或任何库),只要我对它有很好的理解,我都可以接受。 如果有人能阐明这一点并帮助我摆脱这种困惑,我将不胜感激?

【问题讨论】:

  • 我不确定你想要实现什么。Promise 没有 Map 方法。我认为你将所有值存储在 pageFutures 数组中。所以 pageFutures.Map 是正确的......跨度>
  • @kumaran 我试图遵循 cwait 库指令,但它是如此令人困惑。我所需要的只是一步一步地在 pageFutures 中执行承诺。所以应该先解决 pageFutures 中的 3 个承诺,然后再解决下 3 个 ...
  • @kumaran 如果我按照你所说的 cwait 我得到这个: const all=new pageFutures.map( promiseQueue.wrap(()=>{}));这不是一个有效的选项
  • 这里的实际问题是什么?我好像真的说不出来。你能澄清一下你到底想要什么帮助吗?

标签: node.js ecmascript-6 promise es6-promise ecmascript-2016


【解决方案1】:

先说几句:

  • 确实,在您当前的设置中,数据库可能必须同时处理多个批量插入。但是使用Promise.all 并不是引起这种并发性。即使您在代码中遗漏了Promise.all,它仍然会有这种行为。那是因为 Promise 已经创建好了,所以数据库请求会以任何方式执行。

  • 与你的问题无关,但不要使用promise constructor antipattern:当你已经有一个promise时,不需要用new Promise创建一个promise:bulkInsert()返回一个promise ,所以返回那个。

由于您担心数据库负载,我会将pageFutures 承诺发起的工作限制在非数据库方面:它们不必等待彼此的解决方案,因此代码可以保持原样.

让这些承诺解决您当前存储在objects 中的内容:您想要插入的数据。然后将所有这些数组连接到一个大数组中,并将其提供给 one 数据库bulkInsert() 调用。

这可能是这样的:

const pageFutures = myQuery.pages.map(async (pageNumber: number) => {
  const urlObject: any = await this._service.getResultURL(searchRecord.details.id, 
                                                         authorization, pageNumber);
  if (!urlObject.url) { // throw error }
  const data = await rp.get({
    gzip: true,
    headers: { "Accept-Encoding": "gzip,deflate" },
    json: true,
    uri: `${urlObject.url}`,
  });
  // Return here, don't access the database yet...
  return data.objects.filter((object: any) => object.type === "observed-data" 
                                           && object.created);
});
const all: any = await Promise.all(pageFutures).catch(e => {
  console.log(e);
  return []; // in case of error, still return an array
}).flat(); // flatten it, so all data chunks are concatenated in one long array
// Don't create a new Promise with `new`, only to wrap an other promise. 
//   It is an antipattern. Use the promise returned by `bulkInsert`
return this._resultsDatastore.bulkInsert(databaseName, objects);

这使用.flat(),这是相当新的。如果您不支持它,请查看mdn 上提供的替代方案。

【讨论】:

    【解决方案2】:

    首先,您问了一个关于失败的解决方案尝试的问题。那就是X/Y problem

    所以事实上,我理解你的问题,你想延迟一些数据库请求。

    您不想延迟解析由数据库请求创建的Promise...不喜欢!不要尝试! 当数据库返回结果时,promise 将解决。干扰该过程是个坏主意。

    我用你尝试的库撞了一段时间......但我无法解决你的问题。所以我想到了只循环数据并设置一些超时的想法。

    我在这里做了一个可运行的演示:Delaying DB request in small batch

    这里是代码。请注意,我模拟了一些数据和数据库请求。你将不得不适应它。您还必须调整超时延迟。一整秒肯定太长了。

    // That part is to simulate some data you would like to save.
    
    // Let's make it a random amount for fun.
    let howMuch = Math.ceil(Math.random()*20)
    
    // A fake data array...
    let someData = []
    for(let i=0; i<howMuch; i++){
      someData.push("Data #"+i)
    }
    
    console.log("Some feak data")
    console.log(someData)
    console.log("")
    
    // So we have some data that look real. (lol)
    // We want to save it by small group
    
    // And that is to simulate your DB request.
    let saveToDB = (data, dataIterator) => {
      console.log("Requesting DB...")
      return new Promise(function(resolve, reject) {
        resolve("Request #"+dataIterator+" complete.");
      })
    }
    
    // Ok, we have everything. Let's proceed!
    let batchSize = 3 // The amount of request to do at once.
    let delay = 1000  // The delay between each batch.
    
    // Loop through all the data you have.
    for(let i=0;i<someData.length;i++){
    
      if(i%batchSize == 0){
        console.log("Splitting in batch...")
    
        // Process a batch on one timeout.
        let timeout = setTimeout(() => {
    
          // An empty line to clarify the console.
          console.log("")
    
          // Grouping the request by the "batchSize" or less if we're almost done.
          for(let j=0;j<batchSize;j++){
    
            // If there still is data to process.
            if(i+j < someData.length){
    
              // Your real database request goes here.
              saveToDB(someData[i+j], i+j).then(result=>{
                console.log(result)
                // Do something with the result.
                // ...
              })
    
            } // END if there is still data.
    
          } // END sending requests for that batch.
    
        },delay*i)  // Timeout delay.
    
      } // END splitting in batch.
    
    } // END for each data.
    

    【讨论】:

      猜你喜欢
      • 2015-06-24
      • 1970-01-01
      • 2015-11-17
      • 2019-09-29
      • 1970-01-01
      • 1970-01-01
      • 2015-02-25
      • 2022-01-22
      相关资源
      最近更新 更多