How to get sequence from promises from asynchronous iterator?

I start with a function that displays and displays a sequence of images.

async function* renderAll(): AsyncIterableIterator<Blob> { const canvases = await getCanvases(); for (const canvas of canvases) { yield await new Promise<Blob>((resolve, reject) => { canvas.toBlob(result => { if (result) resolve(result); else reject(); }); }); } } 

This works great, but performance is not perfect, because every promise must be resolved before the next operation begins. Instead, the caller must decide when to wait for promises while maintaining order.

 async function* renderAll(): AsyncIterableIterator<Promise<Blob>> { const canvases = await getCanvases(); for (const canvas of canvases) { yield new Promise<Blob>((resolve, reject) => { canvas.toBlob(result => { if (result) resolve(result); else reject(); }); }); } } 

To my surprise, this does not compile because " Blob type is not assigned to Promise<Blob> type." Further inspection reveals that the yield decompresses promises inside an asynchronous function, so yield promise; functionally identical to yield await promise; .

Why does the yield act in this way? Is it possible to infer a sequence of promises from an asynchronous iterator?

+5
source share
2 answers

To get around the amazing behavior of the yield , one possibility is to wrap a promise in a function.

 async function* renderAll(): AsyncIterableIterator<() => Promise<Blob>> { const canvases = await getCanvases(); for (const canvas of canvases) { yield () => new Promise<Blob>((resolve, reject) => { canvas.toBlob(result => { if (result) resolve(result); else reject(); }); }); } } 

I'm not sure this is good practice, but it allows the caller to schedule async to work.

+2
source

For some reason, these two statements seem to have the same effect:

 yield await promise yield promise 

The first statement is actually compiled in yield yield __await(promise) in Javascript, which works as expected. I think the idea is that you should be able to return either an iteration element or a promise of the element, so await not really necessary

From my understanding of the async iterator spec, it should be used in cases where the iteration itself is asynchronous, in your case the iteration itself is not asynchronous, it is rather an asynchronous method that returns the interaction. I would go with:

 async function renderAll(): Promise<Iterable<Promise<IBlob>>> { const canvases = await getCanvases(); return (function* () { for (const canvas of canvases) { yield new Promise<IBlob>((resolve, reject) => { canvas.toBlob(result => { if (result) resolve(result); else reject(); }); }); } })(); } 

OR

 async function renderAll4(): Promise<Iterable<Promise<IBlob>>> { return (await getCanvases()) .map(canvas => new Promise<IBlob>((resolve, reject) => { canvas.toBlob(result => { if (result) resolve(result); else reject(); }); }) ); } 
+2
source

Source: https://habr.com/ru/post/1273750/


All Articles