按顺序解析 Javascript 个承诺列表?
Resolve a list of Javascript Promises - in sequence?
我的实际代码非常复杂,但我会尽可能地简化它:
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()
.. 我不是 reducing/accumulating 这个列表 正如我在关于这个主题的其他问题中看到的那样。我不希望这个因为任何原因被拒绝。
相反,我想要一个结果列表。像这样:
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个承诺?谢谢。
所有这些承诺同时开始,所以您运行并行地
您可以只在数组中包含承诺的 executor
函数 - 然后 运行 reduce
而不是 map
[=16= 中的执行者]
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();
只是针对您的实际问题的一些伪代码。 fn
是您要执行的操作,它应该是 return 一个承诺。
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)
}
}
现有答案提供了 "hand-made" 您可以使用的解决方案。但是对于您的情况,我认为这种方法可以满足您的所有需求:https://caolan.github.io/async/v3/docs.html#parallelLimit
你给它 30k 条记录的列表和 "limit" - 即 10,这意味着它总是保持 10 个任务在执行(一旦一个完成它就会启动另一个)。
如果您需要使用 promise,那么请记住,一旦您创建了 promise,它就会开始在后台执行并且您无法控制它,因此您需要某种 "factory" 方法。这意味着方法在执行时会创建 Promise。
我的实际代码非常复杂,但我会尽可能地简化它:
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()
.. 我不是 reducing/accumulating 这个列表 正如我在关于这个主题的其他问题中看到的那样。我不希望这个因为任何原因被拒绝。
相反,我想要一个结果列表。像这样:
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个承诺?谢谢。
所有这些承诺同时开始,所以您运行并行地
您可以只在数组中包含承诺的 executor
函数 - 然后 运行 reduce
而不是 map
[=16= 中的执行者]
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();
只是针对您的实际问题的一些伪代码。 fn
是您要执行的操作,它应该是 return 一个承诺。
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)
}
}
现有答案提供了 "hand-made" 您可以使用的解决方案。但是对于您的情况,我认为这种方法可以满足您的所有需求:https://caolan.github.io/async/v3/docs.html#parallelLimit
你给它 30k 条记录的列表和 "limit" - 即 10,这意味着它总是保持 10 个任务在执行(一旦一个完成它就会启动另一个)。
如果您需要使用 promise,那么请记住,一旦您创建了 promise,它就会开始在后台执行并且您无法控制它,因此您需要某种 "factory" 方法。这意味着方法在执行时会创建 Promise。