Last active
June 1, 2021 19:10
-
-
Save RickyCook/50fd8caefd5b93baf11625744787c2f5 to your computer and use it in GitHub Desktop.
JS func to get the state of a promise
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') | |
}) | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.