【问题标题】:Resolve a list of Javascript Promises - in sequence?解决 Javascript Promises 列表 - 按顺序?
【发布时间】:2019-10-01 18:04:54
【问题描述】:

我的实际代码非常复杂,但我会尽可能简化:

let counter = 0
console.log("time counter: ", counter)

setInterval(() => {
    counter = counter + 1;
    console.log("time counter: ", counter)
}, 1000)

const myPromises =
  [ new Promise((resolve, reject) => setTimeout(() => {
                console.log("reject(1)")
                reject(1)
        }, 5 * 1000)) // after 5 seconds.

    , new Promise(resolve => setTimeout(() => {
                console.log("resolve(2)")
                resolve(2)
        }, 3 * 1000)) // after 3 seconds.

    , new Promise(resolve => setTimeout(() => {
                console.log("resolve(3)")
                resolve(3)
        }, 3 * 1000))   // after 3 seconds.

    , new Promise((resolve, reject) => setTimeout(() => {
                console.log("reject(4)")
                reject(4)
        }, 1 * 1000))   // after 1 second.

  ]

async function testIt(){
    const results = myPromises.map(async promise => {
            return new Promise((resolve) => {
                // no matter what happens with the individual promise itself, we resolve.
                promise
                    .then(ok => {
                        resolve({ wasSuccessful: true, result: ok })
                    })
                    .catch(err => {
                        resolve({ wasSuccessful: false, error: err })
                    })
            })
    })

    // should be no need to catch anything here. use await.
    const results_ = await Promise.all(results)

    console.log("results_: ", results_)
}

testIt()
    .catch(err => console.log("this error isn't supposed to happen error: ", err))

我基本上想要以下内容:

1. start the first promise( myPromises[0] ). Wait 5 seconds. After that reject it.

2. start the next promise( myPromises[1] ). Wait 3 seconds. Resolve it.

此时我们还有 8 秒的时间。

3. start the next promise( myPromises[2] ). Wait another 3 seconds. Resolve it.

此时我们的计数器有 8 + 3 = 11 秒。

4. start the next promise ( myPromises[3] ).. Wait for 1 second.. resolve it.

我猜你的想法..现在怎么做?

请注意,这不是then().then().then().. 我没有减少/累积此列表,正如我在有关此主题的其他问题中看到的那样。我不想因为任何原因被拒绝。

相反,我想要一个结果列表。像这样:

results_:  [
  { wasSuccessful: false, error: 1 },
  { wasSuccessful: true, result: 2 },
  { wasSuccessful: true, result: 3 },
  { wasSuccessful: false, error: 4 }
]

但请注意我的 console.log 输出 .. 即使 我得到了正确的结果,它也显示了真正的执行顺序:

time counter:  0
time counter:  1
resolve(4)
time counter:  2
resolve(2)
resolve(3)
time counter:  3
time counter:  4
reject(1)
results_:  [
  { wasSuccessful: false, error: 1 },   // note the array ordering is correct. rejected,
  { wasSuccessful: true, result: 2 },    // resolved,
  { wasSuccessful: true, result: 3 },   // resolved,
  { wasSuccessful: false, error: 4 }    // rejected. good.
]
time counter:  5
time counter:  6
time counter:  7

基本上这个promise是并行触发的,无论哪个超时更快,都会更快解决。

相反,我希望它是这样的:

time counter:  0
time counter:  1
time counter:  2
time counter:  3
time counter:  4
time counter:  5
reject(1)
time counter:  6
time counter:  7
time counter:  8
resolve(2)
time counter:  9
time counter:  10
time counter:  11
resolve(3)
time counter:  12
resolve(4)
results_:  [
  { wasSuccessful: false, error: 1 },
  { wasSuccessful: true, result: 2 },
  { wasSuccessful: true, result: 3 },
  { wasSuccessful: false, error: 4 }
]
time counter:  13
time counter:  14
...

这是简化。在实践中,我有 30k+ 条记录的列表——我需要在这些记录上执行一些 api 操作,并从本质上解决一个承诺。 我将此列表分组为每个 10 个元素的子列表。我将并行运行每个子列表。

但是大列表..又名列表列表..需要顺序:

bigList = [ [ small parallel list 0 ], [ small parallel list 1 ] .. ]

这个并行的承诺中的每个承诺已经非常计算密集。 如果我可以并行运行 10 个,我很幸运。所以这就是为什么大列表必须是连续的。否则它会触发一棵有 30k 叶子的承诺树,这会导致崩溃。

仍然不确定这个规模是否现实,但在我实施这个序列之后,我将能够确定。

那么如何依次运行上面的 4 个 promise? 谢谢。

【问题讨论】:

    标签: javascript node.js promise es6-promise


    【解决方案1】:

    所有这些承诺都是同时开始的,所以你是在并行运行它们

    您可以只在数组中拥有 promise 的 executor 函数 - 然后在 reduce 而不是 map 中运行执行程序

    let counter = 0
    console.log("time counter: ", counter)
    let int = setInterval(() => {
        counter = counter + 1;
        console.log("time counter: ", counter)
    }, 1000)
    setTimeout(() => clearInterval(int), 15000);
    const myPromiseExecutors =
      [ (resolve, reject) => setTimeout(() => {
                    console.log("reject(1)")
                    reject(1)
            }, 5 * 1000) // after 5 seconds.
    
        , resolve => setTimeout(() => {
                    console.log("resolve(2)")
                    resolve(2)
            }, 3 * 1000) // after 3 seconds.
    
        , resolve => setTimeout(() => {
                    console.log("resolve(3)")
                    resolve(3)
            }, 3 * 1000)   // after 3 seconds.
    
        , (resolve, reject) => setTimeout(() => {
                    console.log("reject(4)")
                    reject(4)
            }, 1 * 1000)   // after 1 second.
    
      ]
    
    async function testIt(){
        const results = await myPromiseExecutors.reduce(async (promise, exec) => {
            const ret = await promise;
            try {
                const ok = await new Promise(exec);
                ret.push({ wasSuccessful: true, result: ok });
            } catch(err) {
                ret.push({ wasSuccessful: false, error: err });
            }
            return ret;
        }, Promise.resolve([]))
    
    
        console.log("results_: ", results)
    }
    testIt();

    说实话,使用for...of 循环可能会更干净

    let counter = 0
    console.log("time counter: ", counter)
    let int = setInterval(() => {
        counter = counter + 1;
        console.log("time counter: ", counter)
    }, 1000)
    setTimeout(() => clearInterval(int), 15000);
    const myPromiseExecutors =
      [ (resolve, reject) => setTimeout(() => {
                    console.log("reject(1)")
                    reject(1)
            }, 5 * 1000) // after 5 seconds.
    
        , resolve => setTimeout(() => {
                    console.log("resolve(2)")
                    resolve(2)
            }, 3 * 1000) // after 3 seconds.
    
        , resolve => setTimeout(() => {
                    console.log("resolve(3)")
                    resolve(3)
            }, 3 * 1000)   // after 3 seconds.
    
        , (resolve, reject) => setTimeout(() => {
                    console.log("reject(4)")
                    reject(4)
            }, 1 * 1000)   // after 1 second.
    
      ]
    
    async function testIt(){
        const results = [];
        const p = Promise.resolve();
        for (let exec of myPromiseExecutors) {
            try {
                const ok = await new Promise(exec);
                results.push({ wasSuccessful: true, result: ok });
            } catch(err) {
                results.push({ wasSuccessful: false, error: err });
            }
        }
    
        console.log("results_: ", results)
    }
    testIt();

    【讨论】:

    • 很有趣 .. 所以只是我调用 new Promise( executor function here) .. 这已经开始行动了吗?我在想它只有在你调用 then() 或者你使用 await 时才开始。你说我的promises are beginning at the same time - 你的意思是在我的数组中?他们已经开始执行了吗? .. 这很重要的原因是,我可能无法以您在此处显示的这种方式制作此执行者。这只是一个过于简单的例子,实际的代码非常繁琐,可能无法正常工作。我会及时向大家发布。谢谢你现在:)
    【解决方案2】:

    只是一些伪代码,用于显示您的实际问题。 fn 是您要执行的操作,它应该返回一个 Promise。

    async function run_in_chunks(fn, records, chunk_size) {
        for (let chunk of chunkify(records, chunk_size))
           await run_parallel(fn, chunk)
    }
    
    async function run_parallel(fn, records) {
         await Promise.all(records.map(r => run_and_handle_error(fn, r)))
    }
    
    async function run_and_handle_error(fn, record) {
         try {
              await fn(record)
              log_success(record)
         } catch(e) {
              log_error(record)
         }
    }
    

    【讨论】:

      【解决方案3】:

      现有答案提供了您可以使用的“手工”解决方案。但是对于您的情况,我认为这种方法可以满足您的所有需求:https://caolan.github.io/async/v3/docs.html#parallelLimit

      你给它 30k 条记录的列表和“限制” - 即 10 条,这意味着它始终保持 10 个任务执行(一旦完成它就会启动另一个)。

      如果您需要使用 Promise,请记住,一旦您创建了 Promise,它就会开始在后台执行并且您无法控制它,因此您需要某种“工厂”方法。这意味着方法在执行时会创建 Promise。

      【讨论】:

        猜你喜欢
        • 2021-01-18
        • 2019-02-10
        • 1970-01-01
        • 2020-01-03
        • 1970-01-01
        • 1970-01-01
        • 2021-11-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多