看下面代码,为什么使用“错误的处理”那种情况。明明所有hasStock
很快执行完毕并且返回了结果,但是会在判断没货之间,每个判断之间会卡几秒,不是已经有结果了吗。。。
GPT4 说“错误的处理”应该是秒出结果的,它也没搞明白
// 需要订购的产品链接
const productList = [
"https://xxx",
"https://xxx",
"https://xxx"
]
// 是否有货
const hasStock = async (url: string): Promise<boolean> => {
const resp = await mAxios.get(url)
return resp.data.trim() !== ""
}
// 开始订购
const startOrder = async () => {
const promises = productList.map(u => ({tag: u, promise: hasStock(u)}))
// 根据是否有货判断购买
// 正确的处理
const results = await Promise.allSettled(promises.map(p => p.promise))
// 错误的处理
// const results = await Promise.allSettled(promises)
for (const [i, result] of results.entries()) {
// ...
// 是否有货,有就订购
if (!result.value) {
console.log("无货", promises[i].tag)
continue
}
}
1
Plumbiu 350 天前
这个 promises 并不是 Promise 包裹的对象,秒出的结果的状态也是只是 pending
|
2
NessajCN 350 天前 1
Promise.allSettled(p) 函数的参数 p 类型是 Promise[], 而你代码里的 promises 并不是 Promise[], 而是个 Object[],
而该 Object 的 prototype.promise 才是 Promise , 所以要先 map 成 p.promise 才能传给 Promise.allSettled() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled > The Promise.allSettled() static method takes an iterable of promises as input and returns a single Promise. This returned promise fulfills when all of the input's promises settle (including when an empty iterable is passed), with an array of objects that describe the outcome of each promise. |
4
NessajCN 350 天前 1
|
5
lisxour 349 天前
首先 gpt 的回复是对的,确实应该是秒出结果的,而且都是 fulfilled
至于为什么是 fulfilled ,是因为你传给 Promise 执行的是非 Promise ,是一个{ tag, promise }对象,而 Promise 执行非 Promise 时,永远都是 fulfilled ,并且返回值就是传进去的值 至于为什么卡顿,问题并不在循环判断有没有货里面,而是 hasStock 这个方法,因为你进行了 await ,所以下面这行代码其实会卡住的 ``` const promises = productList.map(u => ({tag: u, promise: hasStock(u)})) ``` 你把 hasStock 方法改成这样,你就会发现秒出结果了 ``` const hasStock = async (url: string): Promise<boolean> => { return mAxios.get(url).then(resp => { return resp.data.trim() !== "" }, error => false); } ``` |
7
shuax 349 天前
问题出在 promises 数组中的每个元素应该包含 promise 属性的 Promise 对象,而不是一个对象。
|
11
Vegetable 349 天前
很遗憾,替换网络请求为 sleep 之后无法复现问题
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) // 是否有货 const hasStock = async (url: string): Promise<boolean> => { await sleep(1000) return true } |
12
Vegetable 349 天前
哦靠我用的是你美注释的代码
|
14
lisxour 349 天前
@Plumbiu #13 这行代码会有问题,const promises = productList.map(u => ({tag: u, promise: hasStock(u)}))
|
15
tangmou 349 天前
@lisxour #5 即使 hasStock 里面进行了 `await` 也不影响, 因为对于 `hasStock` 本身没有进行 `await`; 这里 `startOrder` 这个函数不会卡顿 (使用 "错误的处理"), 可能的一个原因是, 通过 node.js 处理时, 它会把所有 pending 的 promise 都处理完, 所以是卡在其他地方了. 比如下面这个代码:
```javascript ``` |
16
tangmou 349 天前
@lisxour #5
@tangmou 代码忘了贴了: ```javascriipt // 需要订购的产品链接 const productList = [ "https://xxx", "https://xxx", "https://xxx" ] async function asyncSleep(duration) { await new Promise(r => setTimeout(r, duration)); } // 是否有货 async function hasStock(url) { await asyncSleep(2000); return false } // 开始订购 const startOrder = async () => { const promises = productList.map(u => ({tag: u, promise: hasStock(u)})) const results = await Promise.allSettled(promises) for (const [i, result] of results.entries()) { // 是否有货,有就订购 if (!result.value) { console.log("无货", promises[i].tag) continue } } } function exitHandler(options, exitCode) { console.log("process exit:", performance.now()); } process.on('exit', exitHandler.bind(null,{cleanup:true})); console.log("before:", performance.now()); startOrder(); console.log("after:", performance.now()); ``` |
19
yin1999 349 天前
这个方法本身允许在可迭代对象中放置非 promise 元素,所以楼上说传参错有问题的其实不对。MDN 文档里面有说:如果传入的 iterable 是非空的,但不包含待定的 promise ,则返回的 promise 仍然是异步兑现的,而不是同步兑现。
所以错误的用法的兑现仍然受到整个运行时的事件循环的影响(调用 allSettled() 方法时会把 CPU 时间片让给其他的异步操作),如果其他操作很占用 CPU 或网络 IO ,那 Promise.allSettled() 本身就不能立刻兑现结果了。你考虑把上面的网络 IO 换成 setTimeout 这种,并减小列表长度,应该就可以“立刻”兑现了。 文档: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled#%E8%BF%94%E5%9B%9E%E5%80%BC |
20
as80393313 348 天前
|