Skip to content

Instantly share code, notes, and snippets.

@RickyCook
Last active June 1, 2021 19:10
Show Gist options
  • Save RickyCook/50fd8caefd5b93baf11625744787c2f5 to your computer and use it in GitHub Desktop.
Save RickyCook/50fd8caefd5b93baf11625744787c2f5 to your computer and use it in GitHub Desktop.
JS func to get the state of a promise
/* Checks the state of a Promise without need to wait for a non-pending result
*
* This is an issue, because the state of a Promise object is inaccessible from
* JavaScript, so we are unable to take actions based on the current state of
* a Promise being pending or not
*/
const PROMISE_STATE_PENDING = Symbol()
async function promiseState(prom) {
try {
const result = await Promise.race([prom, Promise.resolve(PROMISE_STATE_PENDING)])
return result === PROMISE_STATE_PENDING ? 'pending' : 'fulfilled'
} catch(err) {
return 'rejected'
}
}
module.exports = promiseState
const promiseState = require('./promiseState')
describe('promiseState()', () => {
describe.each([['resolve', 'fulfilled'], ['reject', 'rejected']])('on %s', (funcname, state) => {
test(`pending, then ${state}`, async () => {
let resolve, reject
const prom = new Promise(
(...args) => { [resolve, reject] = args }
)
const func = funcname === 'resolve' ? resolve : reject
await expect(utils.promiseState(prom)).resolves.toEqual('pending')
func('testval')
await expect(utils.promiseState(prom)).resolves.toEqual(state)
await expect(prom)[funcname === 'resolve' ? 'resolves' : 'rejects'].toEqual('testval')
})
})
})
@David263
Copy link

David263 commented Jun 1, 2021

It hurts my mind to try to understand asynchronous code, but I know that reading the state of a Promise (like reading the state of a database) is just fine until you try to act on that information, because the information could change before or while you are acting, possibly invalidating the action.

It is notoriously difficult to test for such race conditions unless you use an abstract testing system that tries all possible time relationships between asynchronous operations, including those that could be done by the user.

So what we can achieve is second-best: we can create a function inside a Promise that tries to do a conditional operation, then later reports back to us whether it succeeded in doing the operation. Such operations can fail, which is bad, but they can be made atomic and data-safe, which is good.

One way of allowing operations depending on other operations that works with remote Web operations is to try an operation twice: the first time, trying to "lock" access for the current process and the second time doing the operation and releasing the lock. The problem here is that Promises cannot be locked: when the asynchronous function finishes, it immediately sets the Promise state to Resolved.

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