-1

I seem to be encountering a race condition with a function of mine even though I am not using async functions in it

window.focusElem = (elem, overrideGroupNextFocus) => {
  if (!elem) return
  console.log(`FOCUS: ${elem.id}`)

  if (store.getState().app.isExitVisible &&
    elem.id !== 'exitExitButton' &&
    elem.id !== 'exitBackButton'
  ) {
    return
  }

  // Remove focus class from existing focused elements
  Array.from(document.querySelectorAll('.focus')).forEach(e => {
    console.log(`FOCUS: ${elem.id} removing focus from ${e.id}`)
    e.classList.remove('focus')
  })

  const parentFocusGroup = findParentFocusGroup(elem)
  let elemToFocus = elem
  const focusGroupNextFocusId = parentFocusGroup ? parentFocusGroup.getAttribute('data-focusgroup-next-focus') : ''
  if (!overrideGroupNextFocus &&
    focusGroupNextFocusId &&
    document.activeElement.id !== focusGroupNextFocusId) {
    elemToFocus = parentFocusGroup.querySelector(`#${focusGroupNextFocusId}`) || elem
  }

  store.dispatch(setFocusElem(elemToFocus.id))
  if (elemToFocus.id.startsWith('appNav')) {
    Array.from(document.querySelectorAll('.active')).forEach(e => e.classList.remove('active'))
    elemToFocus.classList.add('active')
    document.querySelector('.app-wrapper').setAttribute('data-sidebar', 'open')
  } else if (!elemToFocus.hasAttribute('data-focus-inmodal')) {
    document.querySelector('.app-wrapper').setAttribute('data-sidebar', 'closed')
  }
  lastFocused = elemToFocus
  console.log(`FOCUS: ${elem.id} add focus to ${elemToFocus.id}`)
  elemToFocus.classList.add('focus')
  document.dispatchEvent(new CustomEvent('app:focusChanged', { detail: elemToFocus }))
}

function findParentFocusGroup(elem) {
  if (elem.hasAttribute('data-focusgroup')) {
    return elem
  }

  const parent = elem.parentNode
  if (parent && parent.nodeType !== 9) return findParentFocusGroup(parent)
}

I notice if this function is called in fast succession, I can get the following output in console:

FOCUS: history0
FOCUS: history0 removing focus from vodBannerProgram
FOCUS: vodBannerProgram
FOCUS: vodBannerProgram add focus to vodBannerProgram
FOCUS: history0 add focus to history0

Notice, it seems like this function is called for history0 first then vodBannerProgram. But it seems like it executed halfway for history0 then proceeded to vodBannerProgram then back again? forEach are synchronous correct?

Jiew Meng
  • 84,767
  • 185
  • 495
  • 805
  • I guess that `store.dispatch` is async – h1b9b Mar 12 '18 at 12:52
  • @h1b9b seems like I should move the `store.dispatch` to the end, but even if its async, should the code below execute after anyways? – Jiew Meng Mar 12 '18 at 13:41
  • Is the prints order always this way (when you do the same actions)? if so it's not race condition and maybe `setFocusElem` case the prints in the middle – Roy Shmuli Mar 12 '18 at 14:02
  • @RoyShmuli I need to do it fast enough for it to get this result, so it sounds like a race condition? – Jiew Meng Mar 13 '18 at 07:38
  • 1. On focus history0, does it will print history0 and vodBannerProgram? 2. If so, When you focus history0, is it print vodBannerProgram somtimes at the end or it always in the middle? – Roy Shmuli Mar 13 '18 at 08:47

1 Answers1

0

There is no race condition without async. You don't display enough code to find it (if this is the case). Check the following code:

  1. setFocusElem
  2. store.dispatch
Roy Shmuli
  • 4,979
  • 1
  • 24
  • 38
  • Hmm ... these are redux specific ... let me try removing that. But even if these are async, it would mean the below code should run immediately after anyways? So I should not be getting this interleaving? – Jiew Meng Mar 12 '18 at 13:39
  • @JiewMeng `store.dispatch` is sync. https://stackoverflow.com/questions/43276291/is-store-dispatch-in-redux-synchronous-or-asynchronous – Bharadwaj Mar 12 '18 at 13:41