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();