Promise.any 在 JS 上的可能实现
Possible implementation of Promise.any on JS
在 Promise.race 中承诺 returns 作为主要承诺 returns。在 Promise.all returns 中,所有承诺都解决了,但持续了一个问题。如果所有承诺中的任何一个被拒绝,所有其他承诺都将被拒绝。
取而代之的是 proposal 对应 Promise.any,returns 每个承诺都单独存在,彼此独立,在拒绝时短路。
const logAfterWait = (seconds) => new Promise((resolve, reject) => {
return setTimeout(() => resolve(console.log(`${time} time passed`)), seconds)
})
const watingList = [
logAfterWait(convertToSeconds(10)),
logAfterWait(convertToSeconds(30)),
logAfterWait(convertToSeconds(5))
]
const logReading = async (fn) => {
console.log(`${time}: reading file`)
await fn()
}
const readFiles = (files) => Promise.all(watingList.map(logReading))
.catch((error) => new Error(error))
这里的问题是Promise.all上的事件循环映射原因块上的事件循环块,同时返回每个结果,与预期结果不同,即5、10、 30 秒。
我可以在 waitingList.map 上避免这种情况吗?
您可以利用 Promise.race
通过创建永不稳定的 Promise
形成幺半群这一事实:
const empty = x => new Promise((res, rej) => x); // never settling promise
const ps = [
Promise.reject(1).catch(empty),
Promise.resolve(2).catch(empty),
Promise.resolve(3).catch(empty)];
Promise.race(ps)
.then(console.log); // 2
您需要为数组中的每个 Promise
附加一个 catch
处理程序。您或许可以创建一个实用函数来为您执行此操作。
你可能会想到这样的事情:
// a solution might just be not using async/await
const any = (promises) => new Promise((resolve, reject) => {
let errors = [];
let resolved;
const onFulfill = (value) => {
// skip if already resolved
if (resolved) { return; }
resolved = true;
// resolve with the first available value
resolve(value);
};
const onError = (error) => {
// skip if already resolved
if (resolved) { return; }
// collect error
errors = errors.concat(error);
// reject promise combinator if all promises are failed
if (errors.length === promises.length) {
reject(errors);
}
};
return promises.forEach((promise) => promise.then(
onFulfill,
onError,
));
});
const sleep = (ms) => new Promise(r => setTimeout(() => r(ms), ms));
const err = (ms) => sleep(ms).then(() => Promise.reject(ms));
// it would log 2000, since it is the first to resolve
any([sleep(3000), err(100), sleep(2000)]).then(console.info)
// it would an array of 2 failures
any([err(50), err(60)]).catch(console.error)
the block of IO
请注意,javascript 中没有任何 IO 块,线程在等待 promise 被解决时可以自由处理任何其他任务。
据此,我得出了一个结论。我们创建了一个解析器,它是一个 Either monad(不是 Either monad 的纯实现),它 returns [err, response] 在 map 函数上。
catch 块是避免未处理的承诺拒绝警告所必需的。
const time = () => `${new Date().getHours()}:${new Date().getMinutes()}:${new Date().getSeconds()}`;
const sleep = (ms, pNumber) => new Promise((resolve, reject) => {
return pNumber < 3
? setTimeout(() => resolve(console.log(`${time()} time passed`)), ms)
: reject(null)
}).catch(null)
Promise.prototype.resolver = async (promise) => {
this._result = await Promise.all([promise])[0];
return this._result == null
? ["The time flies", promise]
: [null, promise]
}
const watingList = [
Promise.resolver(sleep(0, 0).catch(console.error)),
Promise.resolver(sleep(3000, 1).catch(console.error)),
Promise.resolver(sleep(5000, 2).catch(console.error)),
Promise.resolver(sleep(5000, 3).catch(console.error))
]
const logReading = (list) => {
return list.map(p => p.then(console.log(`${time()}: reading file`))
.catch(console.log))
}
((read) => logReading(read))(watingList)
PS: time 函数因计算时间而与预期不同。
在 Promise.race 中承诺 returns 作为主要承诺 returns。在 Promise.all returns 中,所有承诺都解决了,但持续了一个问题。如果所有承诺中的任何一个被拒绝,所有其他承诺都将被拒绝。
取而代之的是 proposal 对应 Promise.any,returns 每个承诺都单独存在,彼此独立,在拒绝时短路。
const logAfterWait = (seconds) => new Promise((resolve, reject) => {
return setTimeout(() => resolve(console.log(`${time} time passed`)), seconds)
})
const watingList = [
logAfterWait(convertToSeconds(10)),
logAfterWait(convertToSeconds(30)),
logAfterWait(convertToSeconds(5))
]
const logReading = async (fn) => {
console.log(`${time}: reading file`)
await fn()
}
const readFiles = (files) => Promise.all(watingList.map(logReading))
.catch((error) => new Error(error))
这里的问题是Promise.all上的事件循环映射原因块上的事件循环块,同时返回每个结果,与预期结果不同,即5、10、 30 秒。
我可以在 waitingList.map 上避免这种情况吗?
您可以利用 Promise.race
通过创建永不稳定的 Promise
形成幺半群这一事实:
const empty = x => new Promise((res, rej) => x); // never settling promise
const ps = [
Promise.reject(1).catch(empty),
Promise.resolve(2).catch(empty),
Promise.resolve(3).catch(empty)];
Promise.race(ps)
.then(console.log); // 2
您需要为数组中的每个 Promise
附加一个 catch
处理程序。您或许可以创建一个实用函数来为您执行此操作。
你可能会想到这样的事情:
// a solution might just be not using async/await
const any = (promises) => new Promise((resolve, reject) => {
let errors = [];
let resolved;
const onFulfill = (value) => {
// skip if already resolved
if (resolved) { return; }
resolved = true;
// resolve with the first available value
resolve(value);
};
const onError = (error) => {
// skip if already resolved
if (resolved) { return; }
// collect error
errors = errors.concat(error);
// reject promise combinator if all promises are failed
if (errors.length === promises.length) {
reject(errors);
}
};
return promises.forEach((promise) => promise.then(
onFulfill,
onError,
));
});
const sleep = (ms) => new Promise(r => setTimeout(() => r(ms), ms));
const err = (ms) => sleep(ms).then(() => Promise.reject(ms));
// it would log 2000, since it is the first to resolve
any([sleep(3000), err(100), sleep(2000)]).then(console.info)
// it would an array of 2 failures
any([err(50), err(60)]).catch(console.error)
the block of IO
请注意,javascript 中没有任何 IO 块,线程在等待 promise 被解决时可以自由处理任何其他任务。
据此,我得出了一个结论。我们创建了一个解析器,它是一个 Either monad(不是 Either monad 的纯实现),它 returns [err, response] 在 map 函数上。
catch 块是避免未处理的承诺拒绝警告所必需的。
const time = () => `${new Date().getHours()}:${new Date().getMinutes()}:${new Date().getSeconds()}`;
const sleep = (ms, pNumber) => new Promise((resolve, reject) => {
return pNumber < 3
? setTimeout(() => resolve(console.log(`${time()} time passed`)), ms)
: reject(null)
}).catch(null)
Promise.prototype.resolver = async (promise) => {
this._result = await Promise.all([promise])[0];
return this._result == null
? ["The time flies", promise]
: [null, promise]
}
const watingList = [
Promise.resolver(sleep(0, 0).catch(console.error)),
Promise.resolver(sleep(3000, 1).catch(console.error)),
Promise.resolver(sleep(5000, 2).catch(console.error)),
Promise.resolver(sleep(5000, 3).catch(console.error))
]
const logReading = (list) => {
return list.map(p => p.then(console.log(`${time()}: reading file`))
.catch(console.log))
}
((read) => logReading(read))(watingList)
PS: time 函数因计算时间而与预期不同。