Skip to content

Instantly share code, notes, and snippets.

@GeKorm
Created February 3, 2024 00:23
Show Gist options
  • Select an option

  • Save GeKorm/043478bc74d3db29d9f3f3a8f4f69907 to your computer and use it in GitHub Desktop.

Select an option

Save GeKorm/043478bc74d3db29d9f3f3a8f4f69907 to your computer and use it in GitHub Desktop.
Cool problem
async function run(elements) {
// ============
// your code here starts here
// We will insert at arbitrary indices in order to preserve result order
const results = Array.from({ length: TOTAL });
const requestPool = [];
let processed = 0;
for (const element of elements) {
const poolSize = requestPool.length;
if (poolSize >= MAX_INFLIGHT || processed >= TOTAL - MAX_INFLIGHT) {
// When the request pool fills up, wait for the fastest response. It will be topped up immediately after.
const [value, index] = await Promise.race(requestPool);
results[index] = value;
}
const request = api(element).then((value) => {
// Remove resolved promise from the pool
requestPool.splice(requestPool.indexOf(request), 1);
// We need a value and index to preserve call order
return [value, elements.indexOf(element)];
});
requestPool.push(request);
processed++;
}
// The first for .. of loop is done when all calls have been made, not when we get the responses
// Here we wait for the last block of promises to settle and iterate the result
// In a real-world program Promise.allSettled() may be preferable. Using Promise.all() for clarity
for (const [value, index] of await Promise.all(requestPool)) {
results[index] = value;
}
return results;
// Real-world implementation would require error handling
// your code ends here
// ============
}
@GeKorm

GeKorm commented Feb 3, 2024

Copy link
Copy Markdown
Author

Full script is

const MAX_INFLIGHT = 4;
const TOTAL = 100;

// the given dummy api supports a maximum of 4 of inflight requests.
// the given code is correct, but it is slow because it processes elements serially.
// your task is to process 100 elements as fast as possible.
// run this code with "node/bun test.js".
// it should print "pass".
// no external dependencies are allowed.
/**
 *
 * @param elements
 */
async function run(elements) {
  // ============
  // your code here starts here
  // We will insert at arbitrary indices in order to preserve result order
  const results = Array.from({ length: TOTAL });
  const requestPool = [];
  let processed = 0;
  for (const element of elements) {
    const poolSize = requestPool.length;
    if (poolSize >= MAX_INFLIGHT || processed >= TOTAL - MAX_INFLIGHT) {
      // When the request pool fills up, wait for the fastest response. It will be topped up immediately after.
      const [value, index] = await Promise.race(requestPool);
      results[index] = value;
    }
    const request = api(element).then((value) => {
      // Remove resolved promise from the pool
      requestPool.splice(requestPool.indexOf(request), 1);
      // We need a value and index to preserve call order
      return [value, elements.indexOf(element)];
    });
    requestPool.push(request);
    processed++;
  }
  // The first for .. of loop is done when all calls have been made, not when we get the responses
  // Here we wait for the last block of promises to settle and iterate the result
  // In a real-world program Promise.allSettled() may be preferable. Using Promise.all() for clarity
  for (const [value, index] of await Promise.all(requestPool)) {
    results[index] = value;
  }
  return results;
  // Real-world implementation would require error handling
  // your code ends here
  // ============
}

// api accepts 1 element, takes some time, returns a processed element
const api = (function () {
  let inflight = 0;
  const sleep = (ms) => new Promise((done) => setTimeout(done, ms));
  return async (element) => {
    inflight++;
    if (inflight > MAX_INFLIGHT) {
      console.log(`too many requests`);
      process.exit(1);
    }
    const delay = Math.random() < 0.1 ? 1000 : 100 + (Math.random() - 0.5) * 100;
    await sleep(delay);
    inflight--;
    console.log(`processed element ${element}/${TOTAL}`);
    return `P${element}`;
  };
})();
// check ensures you have processed all elements correctly
/**
 *
 * @param results
 */
function check(results) {
  for (let i = 0; i < TOTAL; i++) {
    const got = results[i];
    const expect = `P${i + 1}`;
    if (got !== expect) {
      console.log(`expected result[${i}] to be ${expect}, got ${got}`);
      process.exit(1);
    }
  }
  console.log('pass');
}
// create elements, run user code, check results
const elements = Array.from({ length: TOTAL })
  .fill(null)
  .map((_, i) => i + 1);
run(elements).then(check);

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