Javascript 异步生成器:产生被拒绝的承诺
我一直在使用异步生成器,试图创建一个“承诺排序”生成器,它接受一系列承诺,并按照承诺解析或拒绝的顺序逐个生成承诺。比如:Javascript 异步生成器:产生被拒绝的承诺,javascript,promise,async-await,generator,async-iterator,Javascript,Promise,Async Await,Generator,Async Iterator,我一直在使用异步生成器,试图创建一个“承诺排序”生成器,它接受一系列承诺,并按照承诺解析或拒绝的顺序逐个生成承诺。比如: 异步函数*orderProms(prom\u arr){ //复制一份,这样拼接就不会弄乱它。 const proms=[…prom_arr]; while(proms.长度){ //用每个承诺的索引标记它,这样我们就可以在下一个循环中删除它。 const{prom,index}=wait Promise.race(proms.map((prom,index)=>prom.t
异步函数*orderProms(prom\u arr){
//复制一份,这样拼接就不会弄乱它。
const proms=[…prom_arr];
while(proms.长度){
//用每个承诺的索引标记它,这样我们就可以在下一个循环中删除它。
const{prom,index}=wait Promise.race(proms.map((prom,index)=>prom.then(
()=>({prom,index}),
()=>({prom,index})
)));
proms.拼接(索引1);
产量胎膜早破;
}
}
使用这种发电机的想法如下:
const resAfter=(val,delay)=>newpromise(res=>setTimeout(()=>res(val,delay));
const rejAfter=(val,delay)=>newpromise(((uj,rej)=>setTimeout(()=>rej(val,delay));
常量承诺=[
第三次(“第三次”,3000),
第二次(“第一次”,1000),
rejAfter(“第二次”,2000),//注意:这一次拒绝!
];
(异步()=>{
let ordered=orderProms(承诺);
完成=错误;
for(让next_promise=ordered.next();!完成;next_promise=ordered.next()){
const next=等待下一个承诺
.catch(err=>({done:false,value:`catched error:${err}`}));
done=next.done;
如果(!done)console.log(next.value);
}
})()
然而,我注意到,这将达到第二个承诺,然后发电机将停止。这似乎是因为拒绝了“第二次”承诺。当prom
被拒绝时,在生成器中调用yield prom
将在生成器中创建异常
但这是我困惑的根源。我不想在这里创建异常,我只想将拒绝的承诺作为迭代器结果的值
。我不想把它拆开。这几乎就像是被视为yield wait prom
,但正如您所看到的,没有等待
呼叫
这里发生了什么,我怎么能像这个发电机一样简单地给出一个被拒绝的承诺
以下是可运行代码段中的上述代码:
异步函数*orderProms(prom\u arr){
//复制一份,这样拼接就不会弄乱它。
const proms=[…prom_arr];
while(proms.长度){
//用每个承诺的索引标记它,这样我们就可以在下一个循环中删除它。
const{prom,index}=wait Promise.race(proms.map((prom,index)=>prom.then(
()=>({prom,index}),
()=>({prom,index})
)));
proms.拼接(索引1);
产量胎膜早破;
}
}
const resAfter=(val,delay)=>新承诺(res=>setTimeout(()=>res(val,delay));
const rejAfter=(val,delay)=>newpromise(((uj,rej)=>setTimeout(()=>rej(val,delay));
常量承诺=[
第三次(“第三次”,3000),
第二次(“第一次”,1000),
rejAfter(“第二次”,2000),//注意:这一次拒绝!
];
(异步()=>{
let ordered=orderProms(承诺);
完成=错误;
for(让next_promise=ordered.next();!完成;next_promise=ordered.next()){
const next=等待下一个承诺
.catch(err=>({done:false,value:`catched error:${err}`}));
done=next.done;
如果(!done)console.log(next.value);
}
})()
这几乎像是被视为yield wait prom
。这是怎么回事
是异步生成器的行为方式
我怎么能简单地从这个发生器中产生一个被拒绝的承诺呢
你不能。请注意,异步迭代器预期由
try {
for await (const value of orderProms(promises)) {
console.log(value);
}
} catch(err) {
console.error('Caught error: ', err);
}
在语法中没有对单个错误处理的便利。当出现异常时,循环停止,生成器完成。重点
那你能做什么呢?我看到三种选择:
- 只需保持原样,并将早期失败视为一项功能(类似于
)Promise.all
- 处理错误(在orderProms中或在将承诺传递给它之前),并生成承诺状态和值的元组
for await (const value of orderProms(promises.map(prom => prom.catch(err => `Caught error: ${err}`) ))) { console.log(value); }
- 使用一个普通的(非
)生成器,从中手动产生一个又一个承诺,以便能够以您想要的方式使用它异步的
承诺中得到的类似的东西。所有问题都解决了
:
异步函数*orderProms(prom\u arr){
//复制一份,这样拼接就不会弄乱它。
const proms=新集合(prom_arr.map((prom,index)=>({prom,index}));
while(舞会规模){
const settled=wait Promise.race(Array.from)(proms,obj=>obj.prom.then(
value=>Object.assign(obj,{value,status:“completed”}),
error=>Object.assign(obj,{error,状态:“已拒绝”}),
)));
协议删除(已结算);
让{prom,…rest}=已解决;
剩余产量;
}
}
const resAfter=(val,delay)=>新承诺(res=>setTimeout(()=>res(val,delay));
const rejAfter=(val,delay)=>newpromise(((uj,rej)=>setTimeout(()=>rej(val,delay));
常量承诺=[
第三次(“第三次”,3000),
第二次(“第一次”,1000),
rejAfter(“第二次”,2000),//注意:这一次拒绝!
];
(异步()=>{
对于等待(让orderProms的结果(承诺)){
log(JSON.stringify(result));
}
})().catch(err=>console.log(err.message))代码>我不能说公认的答案是错误的,但也不是很正确。尤其是
当出现异常时,循环停止,生成器完成。
重点
这一部分是有问题的
根据您的问题,虽然现代JS允许我们对这个问题做出一些优雅的处理,但按照您的要求,我们仍然可以让它工作,尽管我相信它不是。。。太好了
第一部分-对原始问题的回答
我不会
async function* orderProms(prom_arr) {
// Make a copy so the splices don't mess it up.
var proms = [...prom_arr];
// Tag each promise with it's index, so that we can remove it for the next loop.
try {
while (proms.length) {
var {prom, index} = await Promise.race(proms.map((prom, index) => prom.then(
() => ({prom, index}),
() => ({prom, index})
)));
proms.splice(index, 1);
yield prom;
}
} finally {
proms.length && (ordered = orderProms(proms));
}
}
var resAfter = (val, delay) => new Promise(res => setTimeout(() => res(val), delay)),
rejAfter = (val, delay) => new Promise((_, rej) => setTimeout(() => rej(val), delay)),
promises = [ resAfter("Third", 3000)
, resAfter("First", 1000)
, rejAfter("Second", 2000) // NOTE: this one rejects!
],
ordered = orderProms(promises);
async function endPoint() {
try {
for await (var value of ordered) {
console.log(value)
}
}
catch(e){
console.log(`caught rejection ${e} at endpoint`);
endPoint();
}
}
endPoint();
class SortedPromisesArray extends Array {
constructor(...args){
super(...args);
};
async *[Symbol.asyncIterator]() {
try {
while(this.length){
var {v,i} = await Promise.race(this.map((p,i) => p.then(v => ({v,i}))));
this.splice(i,1);
yield v;
}
} finally {
this.length && this.splice(i,1);
};
};
};
var promise = (val, delay, resolves) => new Promise((v,x) => setTimeout(() => resolves ? v(val) : x(val), delay)),
promises = [ promise("Third", 3000, true)
, promise("First", 1000, true)
, promise("Second", 2000, false) // NOTE: this one rejects!
],
sortedPS = new SortedPromisesArray(...promises);
async function sink() {
try {
for await (let value of sortedPS){
console.log(`Got: ${value}`);
}
} catch(err) {
console.log(`caught at endpoint --> exception ${err}`);
sink();
};
};
sink();
class SortedPromisesArray extends Array {
#RESOLVE;
#REJECT;
#COUNT;
constructor(...args){
super(...args.filter(p => Object(p).constructor === Promise));
this.#COUNT = this.length;
this.forEach(p => p.then(v => this.#RESOLVE(v), e => this.#REJECT(e)));
};
async *[Symbol.asyncIterator]() {
while(this.#COUNT--) {
yield new Promise((resolve,reject) => ( this.#RESOLVE = resolve
, this.#REJECT = reject
));
};
};
};
var promise = (val, delay, resolves) => new Promise((v,x) => setTimeout(() => resolves ? v(val) : x(val), delay)),
promises = [ promise("Third", 3000, true)
, promise("First", 1000, true)
, promise("Second", 2000, false) // NOTE: this one rejects!
],
sortedPS = new SPA(...promises);
async function sink() {
try {
for await (let value of sortedPS){
console.log(`Got: ${value}`);
}
} catch(err) {
console.log(`caught at endpoint --> exception ${err}`);
sink();
}
}
sink();