Как вызвать асинхронную функцию внутри setTimeout, внутри другой асинхронной функции?

#javascript #reactjs #asynchronous #redux #axios

#javascript #reactjs #асинхронный #redux #axios

Вопрос:

Я выполнил действие redux для воспроизведения песни с использованием API проигрывателя Spotify. Я хочу приостановить песню через определенное время, и поэтому я устанавливаю тайм-аут внутри асинхронной функции для вызова другой асинхронной функции после истечения тайм-аута. Однако каждый раз, когда я вызываю первую функцию воспроизведения, время ожидания для второй асинхронной функции, по-видимому, увеличивается без видимой причины. Мой код:

 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 amp;amp; state.spotifyPlayer.controlledPlay amp;amp; 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));
      }
    }
}

  

Ответ №1:

Опубликуйте это, если кто-то столкнется с подобной проблемой. В основном это способ, которым Javascript работает с основным потоком, поскольку js является однопоточным языком, set timeout запускает только функцию, помещенную в него, самое раннее после предусмотренной задержки. Это ненадежно, когда речь идет о коде, который должен выполняться именно в это время.

Чтобы обойти это, вы можете использовать веб-работников, которые работают как бы вне основного потока. Я случайно нашел полезный пакет npm, который уже делает это, поэтому я свяжу его:

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