0

can I await an array length to change to a specified value in JS.

for example:

Suppose there is a magic function named foo here, and I can use foo like this:

async funtion bar () {
    let arr = [];
    // Wait for the arr.length to change to 10, then invoke the handler
    let result = await foo(arr, 10, handler); 
    return result;
}

I mean, when arr.length change to 1, 2, 3...6, 7, 8 or 9..., just wait in this line: let result = await foo(arr, 10, handler); and do NOT return result.

when arr.length change to 10, invoke the handler function, then return result.

Does anyone know how to write this function foo ? or why can not write this function? Thanks


The really thing I want to know is:

We can use await to wait for the result of some asynchronous operations like setInterval or AJAX, but what should I do if I want to wait for a specified value of a "set operation" (set operations: like set in proxy, or setter in Object.defineProperty).

and note that:

I want to know what should I do if I want to "await" a specified set operation., instead of just "watching" a specified set operation

For example keep waiting when arr.length is 0, 1, 2...9, and do something when arr.length is 10

user3840170
  • 26,597
  • 4
  • 30
  • 62
user9186776
  • 1
  • 1
  • 2
  • Don't explain how you want to solve your problem. Explain your *problem*. – Tomalak Jan 08 '18 at 07:57
  • I want to know: we can use await to wait for the result of some asynchronous operations like setInterval or AJAX, but what should I do if I want to wait for a 'set operation'. Like keep waiting when arr.length is 0, 1, 2...9, and do something when arr.length is 10. – user9186776 Jan 08 '18 at 08:05
  • That's still explaining the solution, not the problem you try to solve. What do you want to *do*? Don't tell about the technical way you want to solve something. Tell about what it is you want to solve. – Tomalak Jan 08 '18 at 10:00

3 Answers3

2

You can track changes to the array using a Proxy using a set trap. For each set run a check, and if the check returns true, run the callback (cb) with the array as parameter:

function trackArray(array, check, cb) {
  const arrayChangeHandler = {
    set: function(target, property, value, receiver) {
      target[property] = value;
      // for every set action, run check with the property (length in this case), and the current value
      check(property, value) && cb(target);
      return true;
    }
  };
  
  return new Proxy(array, arrayChangeHandler);
}

const trackedArray = trackArray(
  [], 
  (property, value) => property === 'length' && value === 10, 
  (arr) => console.log(arr)
);

trackedArray.push(1);
trackedArray.push(2);
trackedArray.push(3);
trackedArray.push(4);
trackedArray.push(5);
trackedArray.push(6);
trackedArray.push(7);
trackedArray.push(8);
trackedArray.push(9);
trackedArray.push(10); // this push will invoke the CB
trackedArray.push(11);
trackedArray.push(12);
trackedArray.pop();
trackedArray.pop(); // this pop will invoke the CB
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • thanks, but what should I do if I want to "await" a specified set operation., instead of just "watching" a specified set operation – user9186776 Jan 08 '18 at 08:35
  • You can do whatever you want with callback. For example, wrapping it with a promise, and awaiting the promise. – Ori Drori Jan 08 '18 at 08:48
0

can you check this https://jsfiddle.net/j1kd714y/3/

resolveAfter2Seconds like your foo

function resolveAfter2Seconds(arr, len) {
  return new Promise(resolve => {
    var x = setInterval(() => {
        arr.push(Math.random())
      if(arr.length >= len) {
        clearInterval(x)
        resolve(arr.length);
      }
    }, 500);
  });
}
Aswin Ramesh
  • 1,654
  • 1
  • 13
  • 13
  • Thanks, but I want to await a "set operation", it seems like still await a "setInterval operation" in your case . Could you see my question again, I re-edit it just now to make my question clearer :-) – user9186776 Jan 08 '18 at 08:49
0

I ran into a similar situation recently. I was using arrays for input queuing. I used setImmediate to solve my problem:

const unblock = () => new Promise(setImmediate)

const take = async arr => {
  while (true) {
    if (arr.length > 0) return arr.shift()
    else await unblock()
  }
}

I would use them in functions like this:

const asyncInputOutput = (src, trg) => async () => {
  while (true) {

    // do stuff ...

    if (/* need input */) {
      const data = await take(src)
    }
    if (/* produce output */) {
      trg.push(someData)
    }
  }
}

And, use that function like this:

const main = async () => {
  const reg = { a: [], b: [], c: [] }

  const machine1 = asyncInputOutput(reg.a, reg.b)
  const machine2 = asyncInputOutput(reg.b, reg.c)

  await Promise.all([machine1(), machine2()])

  return reg.c // or whatever
}

This was for the Intcode during 2019 Advent of Code challenges. Given a set of instructions (as integers) the machine can do simple arithmetic to eventually something like a cli text game. To cut to the chase, there was a problem with multiple machines running since one would have to pause if its input queue was empty.


To answer your specific question, something like this might work:

const unblock = () => new Promise(setImmediate)

const waitForLength = async (arr, len, handler) => {
  while (true) {
    if (arr.length >= len) return handler(arr)
    else await unblock()
  }
}

const asyncDoThings = async () => {
  const arr = []

  // ... do things

  const result = await waitForLength(arr, 10, handler)
  return result
}
Kevin
  • 401
  • 2
  • 9