DEV Community

Discussion on: Would it make sense to have "Symbol.promise" as standard JavaScript Symbol?

Collapse
 
noseratio profile image
Andrew Nosenko

The close method on subscription might be useful for scenarios like this:

const subscription = createSubscription(
  cancellationToken, eventSource, "eventName"); 
try {
  await Promice.race([
    subscription[Symbol.promise], 
    anotherPromise
  ]);
}
finally {
  subscription.close();
}
Enter fullscreen mode Exit fullscreen mode

In which case, if anotherPromise wins the race, I want to synchronously stop subscription.

As to cancellation, I currently use Prex library, for its close resemblance with .NET cancellation framework. I mentioned that in the TC39 cancellation discussion thread. Indeed, it's hard to predict what the final standard will be and it may take years before it reaches stage 4, so I'm just using something that is available today.

Collapse
 
spyke profile image
Anton Alexandrenok • Edited

If your close is my cancel and cancelling an already settled Promise makes no harm:

const subscriptionData = getSubscriptionData(
  cancellationToken, eventSource, "eventName"); 

try {
  await Promise.race([
    subscriptionData, 
    anotherPromise
  ]);
} finally {
  cancellationToken.cancel();
}
Enter fullscreen mode Exit fullscreen mode

But with finalized Cancellation API it could be something completely different.

I also could give you another idea for fun:

class AsyncOp {
  #resolve = null;
  #reject = null;
  constructor() {
    return new Promise((res, rej) => {
      this.#resolve = res;
      this.#reject = rej;
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Now it's officially a Promise without needing a global Symbol ))

Thread Thread
 
noseratio profile image
Andrew Nosenko • Edited

If your close is my cancel and cancelling an already settled Promise makes no harm:

I like your ideas, but the thing is, in the lacks of the standard cancellation framework for JavaScript, we all use different libraries. So, what works for you might not work for me :)

The prior art behind the current TC39 cancellation proposal is the .NET cancellation model, and it's also what's behind Prex, the library I use. It has two separate but connected concepts: CancellationTokenSource (the producer side of the API) and CancellationToken (the consumer part of it). Cancellation can be initiated on the source only, and observed on the token.

That makes sense, because cancellation is external to an API. But this way, using cancellation for stopping subscriptions gets a bit bulky, because I now need to create a temporary, linked token source just for the scope of my subscription, only to be able to cancel it (here is the complete runkit):

async function testSubscription(token) {
    const tokenSource = new prex.CancellationTokenSource([token]);
    const s = createSubscription(tokenSource.token);
    const p = prex.delay(prex.CancellationToken.none, 500); 
    try {
        await Promise.race([s, p]);
    } 
    finally {
        tokenSource.cancel();
        tokenSource.close();
    }
}
Enter fullscreen mode Exit fullscreen mode

Personally, I'd rather stick to using the subscription.close pattern, which is less verbose and reduces the number of allocations:

async function testSubscription(token) {
    const s = createSubscription(token);
    const p = prex.delay(prex.CancellationToken.none, 500); 
    try {
        await Promise.race([s.promise, p]);
    } 
    finally {
        s.close();
    }
}
Enter fullscreen mode Exit fullscreen mode

If there was standard Symbol.promise, it'd be Promise.race([s[Symbol.promise], p]), which I still think isn't too bad.

Your mileage with this may vary, depending on the libraries you use.

I also could give you another idea for fun [class AsyncOp]:

Prex has Deferred class for that, and I use it a lot :) It's semantically close to .NET TaskCompletionSource.

Thanks for the discussion!