0

I'm attempting to define a function that returns a promise. The promise should resolve when a given array is set (push()).

To do this I'm attempting to use a Proxy object (influenced by this):

let a = []

;(async function(){

  const observe = array => new Promise(resolve =>
      new Proxy(array, {
          set(array, key, val) {
              array[key] = val;
              resolve();
            }
      }));

  while(true){

      await observe(a);
      console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:",`${a.pop()}`);
    }

})(a);

;(async function(){
    await new Promise(resolve => timerID = setTimeout(resolve, 2000))
    a.push('ʕ·͡ᴥ·ʔ');
    a.push('¯\(°_o)/¯ ')
})(a)

I can't see why this doesn't work. Does anyone have any idea?

More generally, what is a good way to have a promise resolve on push to an array?

21
  • You can take a look at the following answer stackoverflow.com/questions/55309894/… Commented Jan 20, 2020 at 10:55
  • Thanks @Avior, despite its title that answer is about promises on a fetch command Commented Jan 20, 2020 at 10:59
  • 1
    Properly building Javascript proxy set handlers for arrays Commented Jan 20, 2020 at 11:04
  • I don't think await observe(a); will ever resolve... Commented Jan 20, 2020 at 11:08
  • 1
    ;(async function() { ... })(a); - Passing a into the IIFE but not using it is useless. And you would have to store the result of new Proxy() as a Commented Jan 20, 2020 at 11:14

1 Answer 1

3

The problems with your attempt:

  • you invoke .push on the original array, not the proxied one. Where you create the proxy, it is returned to no-one: any reference to it is lost (and will be garbage collected).
  • The code following after the line with await will execute asynchronously, so after all of your push calls have already executed. That means that console.log will execute when the array already has two elements. Promises are thus not the right tool for what you want, as the resolution of a promise can only be acted upon when all other synchronous code has run to completion. To get notifications during the execution synchronously, you need a synchronous solution, while promises are based on asynchronous execution.

Just to complete the answer, I provide here a simple synchronous callback solution:

function observed(array, cb) {
    return new Proxy(array, {
        set(array, key, val) {
            array[key] = val;
            if (!isNaN(key)) cb(); // now it is synchronous
            return true;
        }
    });
}

let a = observed([], () =>
    console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:", `${a.pop()}`)
);

a.push('ʕ·͡ᴥ·ʔ');
a.push('¯\(°_o)/¯ ');

As noted before: promises are not the right tool when you need synchronous code execution.

When each push is executed asynchronously

You can use promises, if you are sure that each push happens in a separate task, where the promise job queue is processed in between every pair of push calls.

For instance, if you make each push call as part of an input event handler, or as the callback for a setTimeout timer, then it is possible:

function observed(array) {
    let resolve = () => null; // dummy
    let proxy = new Proxy(array, {
        set(array, key, val) {
            array[key] = val;
            if (!isNaN(key)) resolve();
            return true;
        }
    });
    proxy.observe = () => new Promise(r => resolve = r);
    return proxy;
}


let a = observed([]);
(async () => {
    while (true) {
        await a.observe();
        console.log(new Date().toLocaleTimeString(),"Blimey Guv'nor:",`${a.pop()}`);
    }
})();

setTimeout(() => a.push('ʕ·͡ᴥ·ʔ'), 100);
setTimeout(() => a.push('¯\(°_o)/¯ '), 100);

Sign up to request clarification or add additional context in comments.

4 Comments

Great points. The first really helped me understand my error. re. the second - I was not clear in the question (the array pushes should have been via the console, or after a delay). I'll adjust the question, thanks.
After your edit, the two push calls are still executed in the same synchronous piece of code (even though that piece itself executed asynchronously). There is no way a promise resolution would get in-between those two push calls. They really need to be each in a separate asynchronous job for a promise to have any chance to trigger a job in-between.
See addition to answer.
Superb! In my use case, many of the push calls will indeed be asynchronous (again should have made that clearer). Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.