[go: up one dir, main page]

Skip to content

Instantly share code, notes, and snippets.

@jakearchibald
Last active March 25, 2024 11:36
Show Gist options
  • Save jakearchibald/d0b7e65496a8ec362f10739c3e28da6e to your computer and use it in GitHub Desktop.
Save jakearchibald/d0b7e65496a8ec362f10739c3e28da6e to your computer and use it in GitHub Desktop.
async function mergeResponses(responsePromises, headers) {
const readers = responsePromises.map(p => Promise.resolve(p).then(r => r.body.getReader()));
let doneResolve;
let doneReject;
const done = new Promise((r, rr) => {
doneResolve = r;
doneReject = rr;
});
const readable = new ReadableStream({
async pull(controller) {
const reader = await readers[0];
try {
const {done, value} = await reader.read();
if (done) {
readers.shift();
if (!readers[0]) {
controller.close();
doneResolve();
return;
}
return this.pull(controller);
}
controller.enqueue(value);
}
catch (err) {
doneReject(err);
throw err;
}
},
cancel() {
doneResolve();
}
});
const response = await responsePromises[0];
return {
done,
response: new Response(readable, {
headers: headers || response.headers
})
};
}
// This depends on transform streams & async iterators
async function mergeResponses(responsePromises, headers) {
const {readable, writable} = new TransformStream();
const done = (async function() {
for await (const response of responsePromises) {
await response.body.pipeTo(writable, {preventClose: true});
}
writable.getWriter().close();
})();
return {
done,
response: new Response(readable, {
headers: headers || (await responsePromises[0]).headers
})
};
}
@triblondon
Copy link
triblondon commented May 31, 2017

Maybe a couple of bugs in this? Not sure if you can do a PR to a gist, but I had to change these:

Line 27:

controller.enqueue(value);

Line 41:

return {
  done,
  response: new Response(readable, {
    headers: headers || response.headers
  })
};

Thanks for writing this. Very useful for a demo I'm doing for a talk this weekend!

@jakearchibald
Copy link
Author

Ah, well spotted!

@PRMSA
Copy link
PRMSA commented Dec 26, 2022

can you explain why use promises here?

const readers = responsePromises.map(p => Promise.resolve(p).then(r => r.body.getReader()));

instead of using simple array?

const readers = responsePromises.map(p => p.body.getReader()

@jakearchibald
Copy link
Author

@PRMSA it allows responsePromises to be a mix of Promise<Response> and plain Response.

@lemanschik
Copy link
lemanschik commented Dec 26, 2023

future2.js

const mergeResponses = async (
  responsePromises = [Promise.resolve(new Response())], 
  headers = new Headers()
) => new Response(new ReadableStream({ async start(controller) {
  for await (const response of responsePromises) {
    // Bonus example: controller.enqueue(await response.text());
    await response.body.pipeTo(new WritableStream({
      write: (data) => controller.enqueue(data)
    }));
  }
}}),{
  headers: headers.has("Content-Type") ? headers : (await responsePromises[0]).headers
});

// usage example
globalThis.onfetch = (event) => event.respondWith(mergeResponses([...responsePromises]));

The function parameters got set with empty default values for typeInferencing in IDE's
so the above example is 100% Typed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment