简体   繁体   中英

How to call an async function inside setTimeout, inside another Async function?

I made a redux action to play a song using the Spotify player's API. I want to pause the song after a certain amount of time and so I set a timeout inside the async function to call another async function after the timeout ends. However every time I call the first play function the timeout for the second async function seems to grow for apparently no reason. My code:

let timeoutId

...

export const playElement = (accessToken, songUri, refreshToken, start, duration, deviceId) => async (dispatch) => {
  dispatch(loading());
  clearTimeout(timeoutId)
  timeoutId = setTimeout(async () => {
    await dispatch(pausePlayer(accessToken, refreshToken, deviceId, songUri))
    
  }, duration*1000)

 
  try { 
    const url = deviceId === '' ? '/me/player/play' : `/me/player/play?device_id=${deviceId}`;
    await spotify.put(
      url,
      { position_ms: start * 1000, uris: [songUri] },

      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
      }
    );
    
    dispatch({
      type: PLAY,
      playing: true,
    })
  } catch (error) {
    dispatch(returnErrors(error));
    if (error.response.status === 401) {
      const newAccessToken = await dispatch(refreshAccessToken(refreshToken));
      dispatch(playElement(newAccessToken, songUri, refreshToken, start, duration, deviceId));
    }
    if (error.response.status === 404) {
      const newDeviceId = await dispatch(getDeviceId(accessToken));
      dispatch(playElement(accessToken, songUri, refreshToken, start, duration, newDeviceId));
    }
  }
  
  dispatch(notLoading());

};

export const pausePlayer = (accessToken, refreshToken, deviceId, songUri) =>  async (dispatch, getState) => {
  try { 
    let state = getState();
    const url = deviceId === '' ? '/me/player/pause' : `/me/player/pause?device_id=${deviceId}`;
    if (state.spotifyPlayer.playing && state.spotifyPlayer.controlledPlay && state.spotifyPlayer.song === songUri) { 
      await spotify.put( 
        url,
        {}, {
          headers: {
            Authorization: `Bearer ${accessToken}`,
          },
        }
      )
  }
    dispatch({
      type: PAUSE,
      payload: false
    })

    } catch (error) {
      dispatch(returnErrors(error));
      if (error.response.status === 401) {
        const newAccessToken = await dispatch(refreshAccessToken(refreshToken));
        dispatch(pausePlayer(newAccessToken, deviceId));
      }
      if (error.response.status === 404) {
        const newDeviceId = await dispatch(getDeviceId(accessToken));
        dispatch(pausePlayer(accessToken, newDeviceId));
      }
    }
}

Posting this if someone runs into a similar problem. Basically this is the way Javascript works with the main thread since js is a single-threaded language, set timeout will only run the function placed in it at the earliest after the provided delay. This is not reliable when it comes to code that needs to be executed exactly at that time.

To get around that you can use web workers which work sort of outside the main thread. I happened to have found a useful npm package that does this already so I'll link it:

https://www.npmjs.com/package/worker-timers

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM