Skip to content

Commit

Permalink
add Q.safetyAwait
Browse files Browse the repository at this point in the history
a function that preserves stack when continuing promises around other promises
  • Loading branch information
splitice committed Nov 29, 2022
1 parent f1e32ed commit 4d7fd5e
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 0 deletions.
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,48 @@
# q-lite
Lightweight implementation of Q.js for performance

## Methods

### Q.safetyAwait

Solves stack preservation when calling async functions without an await over async boundries

```
function testFn(){
const deferred = Q.defer()
async function a(){
await Q.delay(1)
throw new Error('test')
}
const promise = a()
// will fail if this becomes awaited
// this is because the stack is not preserved
await Q.delay(10)
const p = Q.cancelledRace([deferred.promise,promise])
deferred.resolve()
return await p
}
```

Stack would be lost (`testFn`) in this test over the `Q.delay` call. However with `Q.safetyAwait` method state can be preserved.

```
function testFn(){
const deferred = Q.defer()
async function a(){
await Q.delay(1)
throw new Error('test')
}
const promise = a()
// will fail if this becomes awaited
// this is because the stack is not preserved
await Q.safetyAwait([Q.delay(10)], [promise])
const p = Q.cancelledRace([deferred.promise,promise])
deferred.resolve()
return await p
}
```
35 changes: 35 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,4 +415,39 @@ Q.singularize = function(fn){
}
}


async function safeyAwait(cancelFn, primaryValues, secondaryValues){
let dd
for(let i = 0; i < secondaryValues.length; i++){
dd = Q.defer()
const promise = secondaryValues[i]
promise.then(dd.resolve, dd.reject)
dd.promise.resolve = dd.resolve
secondaryValues[i] = dd.promise
}

let deferred = dd
if(!deferred) {
deferred = Q.defer()
deferred.promise.resolve = deferred.resolve
secondaryValues.push(deferred.promise)
}
cancelFn(()=>deferred.reject(Q.CancellationError))

try {
await Promise.race([...primaryValues, ...secondaryValues])
} finally {
for(const p of secondaryValues){
p.resolve()
}
}
}

Q.safeyAwait = function(primaryValues, secondaryValues) {
let cancel
const ret = safeyAwait(c=>cancel=c, primaryValues, secondaryValues)
ret.cancel = cancel
return ret
}

module.exports = Q
66 changes: 66 additions & 0 deletions test/q-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,71 @@ describe('Q tests', function(){
expect(ex.stack.toString()).to.contain('testFn')
}
})
it('should preserve stack (2)', async function(){
async function testFn(){
const deferred = Q.defer()
async function a(){
await Q.delay(1)
throw new Error('test')
}
const promise = a()

// will fail if this becomes awaited
// this is because the stack is not preserved

//await Q.delay(10)
const p = Q.cancelledRace([deferred.promise,promise])
deferred.resolve()
return await p
}
try {
await testFn()
} catch(ex){
expect(ex.stack.toString()).to.contain('testFn')
}
})
it('should preserve stack (3)', async function(){
async function testFn(){
const deferred = Q.defer()
async function a(){
await Q.delay(1)
throw new Error('test')
}
const promise = a()

// unlike test 2 this will work, because the stack is preserved, however if promise is never resolved then promise will leak
await Promise.race([Q.delay(10), promise])

const p = Q.cancelledRace([deferred.promise,promise])
deferred.resolve()
return await p
}
try {
await testFn()
} catch(ex){
expect(ex.stack.toString()).to.contain('testFn')
}
})
it('should preserve stack (4)', async function(){
async function testFn(){
const deferred = Q.defer()
async function a(){
await Q.delay(1)
throw new Error('test')
}
const promise = a()

await Q.safeyAwait([Q.delay(10)], [promise])

const p = Q.cancelledRace([deferred.promise,promise])
deferred.resolve()
return await p
}
try {
await testFn()
} catch(ex){
expect(ex.stack.toString()).to.contain('testFn')
}
})
})
})

0 comments on commit 4d7fd5e

Please sign in to comment.