Skip to content

Commit 23cb559

Browse files
fix: memoize promise context propagation to avoid safari hangs (#1597)
1 parent d6985ab commit 23cb559

File tree

2 files changed

+32
-6
lines changed

2 files changed

+32
-6
lines changed

src/common/wrap/wrap-promise.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,23 @@ export function wrapPromise (sharedEE) {
140140

141141
promiseEE.on('propagate', function (val, overwrite, trigger) {
142142
if (!this.getCtx || overwrite) {
143-
this.getCtx = function () {
144-
// eslint-disable-next-line
145-
if (val instanceof Promise) {
146-
var store = promiseEE.context(val)
143+
const selfStore = this
144+
const parentStore =
145+
val instanceof Promise ? promiseEE.context(val) : null
146+
let cachedCtx
147+
148+
this.getCtx = function getCtx () {
149+
if (cachedCtx) return cachedCtx
150+
151+
if (parentStore && parentStore !== selfStore) {
152+
cachedCtx =
153+
typeof parentStore.getCtx === 'function'
154+
? parentStore.getCtx()
155+
: parentStore
156+
} else {
157+
cachedCtx = selfStore
147158
}
148-
149-
return store && store.getCtx ? store.getCtx() : this
159+
return cachedCtx
150160
}
151161
}
152162
})

tests/components/wrappings/wrap-promise.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ test('Promise then chains are kept separate and distinct', async () => {
7777
await promise
7878
})
7979

80+
test('Propagation caches resolved parent context once per chain', async () => {
81+
const sentinelCtx = {}
82+
const parentPromise = new Promise(resolve => resolve('parent'))
83+
const parentStore = promiseEE.context(parentPromise)
84+
parentStore.getCtx = jest.fn(() => sentinelCtx)
85+
86+
const childPromise = parentPromise.then(() => {})
87+
const childStore = promiseEE.context(childPromise)
88+
89+
expect(childStore.getCtx()).toBe(sentinelCtx)
90+
expect(childStore.getCtx()).toBe(sentinelCtx)
91+
expect(parentStore.getCtx).toHaveBeenCalledTimes(1)
92+
93+
await childPromise
94+
})
95+
8096
const thrownError = new Error('123')
8197
test('A promise constructor exception can be caught', async () => {
8298
await new Promise(function (resolve, reject) {

0 commit comments

Comments
 (0)