【问题标题】:Collect promises generated within promises in forEach在 forEach 中收集 Promise 中生成的 Promise
【发布时间】:2019-01-08 14:09:20
【问题描述】:

我想显示迁移操作到 mongodb 的进度。

脚本如下:

let promises = [];
mylist.forEach(idx => {
    myCollection.find({id: idx}).toArray().then(msgs => {
        promises.push(myCollection2.insertMany(msgs.map(msg => ({
            msg: msg,
            otherFields: others
        }))))
    })
});

// function to display the progress:
allProgress(promises,
  (p) => {
     console.log(`% Done = ${p.toFixed(2)}`);
});
function allProgress(proms, progress_cb) {
    let d = 0;
    progress_cb(0);
    proms.forEach((p) => {
      p.then(()=> {    
        d ++;
        progress_cb( (d * 100) / proms.length );
      });
    });
    return Promise.all(proms);
}

这不起作用,因为在调用allProgress()promises 为空。

在调用allProgress()之前如何正确收集所有的promise?


更新

在制作MCVE的过程中,我想出了

let promises = [];
[1,2,3].forEach(idx => {

    test(1000).then(promises.push(test(10000)));
});

console.log(promises.length);
// function to display the progress:
allProgress(promises,
  (p) => {
     console.log(`% Done = ${p.toFixed(2)}`);
});

function test(ms) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(`Waited ${ms}`);
            resolve();
        }, ms);
    });
}

function allProgress(proms, progress_cb) {
    let d = 0;
    progress_cb(0);
    proms.forEach((p) => {
        p.then(() => {
            d++;
            progress_cb((d * 100) / proms.length);
        });
    });
    return Promise.all(proms);
}

令我惊讶的是,这个脚本可以运行...为什么不等同于我的原始脚本?


更新2

[1,2,3].forEach(idx => {
    test(1000).then(_ => {
        promises.push(test(10000))
    });
});

这应该是 MCVE,它不起作用。

【问题讨论】:

  • 为什么不使用map 而不是forEach + push?这也将防止你的错误

标签: javascript mongodb promise


【解决方案1】:

.find() 函数是异步的,因此当您仍在寻找元素的过程中时,forEach 循环本身会继续运行。最后,您最终会等待您的 .find()。

您可以做的是在 .then() 回调中检查当前 forEach 项的索引,如果您在最后一项,那么我们知道所有承诺都已返回。所以在那里调用你的 allProgress 函数。

这应该留出足够的时间来等待所有内容融合在一起。此外,通过检查索引,我们知道我们只会在完成时调用您的 allPromises 函数。每个 forEach 循环都不会发生多次。

let promises = [];
mylist.forEach((idx, index) => {
    myCollection.find({id: idx}).toArray().then(msgs => {
        promises.push(myCollection2.insertMany(msgs.map(msg => ({
            msg: msg,
            otherFields: others
        }))));
        if((index + 1) === mylist.length){
            // function to display the progress:
            allProgress(promises, (p) => {
                console.log(`% Done = ${p.toFixed(2)}`);
            });
        }
    })
});

function allProgress(proms, progress_cb) {
    let d = 0;
    progress_cb(0);
    proms.forEach((p) => {
      p.then(()=> {    
        d ++;
        progress_cb( (d * 100) / proms.length );
      });
    });
    return Promise.all(proms);
}

编辑: 您的 MCVE(最新编辑)因完全相同的原因而失败。您的请求是异步的,这使循环无需等待即可进行。再次检查索引并在完成后调用。

let promises = [];
let entries = [1, 2, 3]
entries.forEach((idx, index) => {
    test(1000).then(_ => {
        promises.push(test(10000))
        if((index + 1) === entries.length) {
          console.log(promises.length);
          // function to display the progress:
          allProgress(promises,
            (p) => {
               console.log(`% Done = ${p.toFixed(2)}`);
          });
         }
    });
});

function test(ms) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(`Waited ${ms}`);
            resolve();
        }, ms);
    });
}



function allProgress(proms, progress_cb) {
    let d = 0;
    progress_cb(0);
    proms.forEach((p) => {
        p.then(() => {
            d++;
            progress_cb((d * 100) / proms.length);
        });
    });
    return Promise.all(proms);
}

【讨论】:

  • 注意,有一点错字,应该是 index + 1。在示例中已修复。
【解决方案2】:

myCollection.find({id: idx}) 是异步操作。 所以你可以喜欢这样:

let promises = [];
mylist.forEach(idx => {
    myCollection.find({id: idx}).toArray().then(msgs => {
       promises.push(myCollection2.insertMany(msgs.map(msg => ({
           msg: msg,
           otherFields: others
       }))))
      allProgress(promises,
        (p) => {
            console.log(`% Done = ${p.toFixed(2)}`);
      });
  })
});
function allProgress(proms, progress_cb) {
   let d = 0;
    progress_cb(0);
   proms.forEach((p) => {
     p.then(()=> {    
       d ++;
       progress_cb( (d * 100) / proms.length );
     });
   });
   return Promise.all(proms);
}

【讨论】:

  • 这将导致 allPromises 在 forEach 的每个循环中被调用,这可能导致相同的 Promise 被多次执行。
猜你喜欢
  • 2017-10-24
  • 2018-10-01
  • 2020-11-13
  • 2022-11-10
  • 2018-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多