Hacker News new | past | comments | ask | show | jobs | submit login

Weird API to release the lock. What if you want to hold on to it? Then you need to do some silly promise wrapper. Would be better if there was a matching release() function.



The ergonomics here for 99.999% of uses seem great to me. Whatever async function you have can run for as long as it needs. That's using the language, not adding more userland craft. It's a good move.


Probably because there is no RAII semantics in JS and they don’t want to allow forgetting releasing the lock. Although the promise workaround is explicitly opting into this behavior


Javascript in browsers already has a full atomics API:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

I'm not sure why Web Locks is useful TBH. I guess if you don't understand atomics it's a friendlier API?


The Web Locks documentation explains that it works across tabs, i.e. separate processes:

”[…] allows a web app running in multiple tabs or workers to coordinate work and the use of resources”

A locking API is much more natural and less error-prone for this use case than using shared memory and atomics.


You mean Atomics + SharedArrayBuffer, otherwise it won't be shared across agents. I can imagine all the postMessage calls and message handlers swirling around in all agents to approximate something like the Web Locks API for a simple lock, but tbh I'd take the Web Locks API any day.


It's "atomic" in the sense that it does one thing. It lets you do "actions", not "transactions". A transaction would allow you to do multiple things.

If the atomics API gave you ability to do multiple things, you wouldn't need compareExchange, because you could just do compare and then exchange.


  p = Promise.withResolvers()
  navigator.locks.request(
    "foo",
    p.promise,
  )
  p.resolve()
I guess there’s room for .requestWithResolvers() still, they rarely learn the first lesson. Even the $subj article seems to be unaware of it and uses the silly wrapper way.


  class Lock {
    #release: () => void | undefined;
    constructor(public name: string) {}
    acquire() {
      if (this.#release !== undefined) throw new Error("Already locked");
      navigator.locks.request(this.name, async (lock) => {
        await new Promise<void>(resolve => {
          this.#release = resolve;
        });
      });
    }
    release() {
      if (this.#release === undefined) throw new Error("Not locked");
      this.#release();
    }
  }


You're not treating the aquire phase correctly:

  class Lock {
    #resolve: (() => void) | undefined;
    #releasePromise: Promise<void> | undefined;

    constructor(public name: string) {}

    acquire(): Promise<void> {
      if (this.#releasePromise !== undefined) {
        throw new Error("Already aquired");
      }

      return new Promise((resolveAquire, _rejectAquire) => {
        this.#releasePromise = new Promise((resolveRelease, _rejectRelease) => {
          navigator.locks.request(this.name, async (lock) => {
            await new Promise<void>((resolve, _reject) => {
              this.#resolve = resolve;
              resolveAquire();
            });

            resolveRelease();
          });
        });
      });
    }
    
    async release(): Promise<void> {
      if (this.#releasePromise === undefined) {
        throw new Error("Already released");
      }

      this.#resolve();
      await this.#releasePromise;

      this.#resolve = undefined;
      this.#releasePromise = undefined;
    }
  }
...the release phase still feels off without a Promise, but maybe somebody else can tackle that :D

EDIT: think I fixed it, untested though


ah! nice




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: