异步/等待不等待Promise的解决

5

看起来我的使用 await 的方式并不像我理解的那样。

我认为一个 async 函数和 await 关键字可以用于返回 Promise 的调用(例如 navigator.mediaDevices.getUserMedia),它会暂停函数执行(就像生成器函数一样),直到 Promise 解决,然后继续执行函数。

尽管我正在等待调用,但它立即返回,我的控制台日志的顺序出现了问题。特别是以“reducer”开头的控制台日志先于以“getter”开头的控制台日志出现,而根据堆栈跟踪运行的方式应该相反/与我(认为)的运行方式相反。

有人能告诉我这里发生了什么吗?

异步辅助函数 getter:

const getLocalStream = async () => {
  try {
    const localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
    console.log('getter localStream', localStream);
    console.log('about to return')
    return localStream;
  }
  catch (err) {
    console.error(err);
  }
}

export default getLocalStream;

Reducer/store:

import getLocalStream from './getLocalStream';

const reducer = (state = {}, action) => {
  switch (action.type) {
    case 'SEND_CALL': {
      const { id } = action;
      const localStream =  getLocalStream();
      console.log('reducer localStream', localStream);
      const call = state.peer.call(id, localStream);
      return { ...state, call };
    }
    default: 
      return state;
  }
};

控制台:

reducer localStream Promise {<pending>}
Uncaught TypeError: Failed to execute 'addStream' on 'RTCPeerConnection': parameter 1 is not of type 'MediaStream'
getter localStream MediaStream {id: "B6Foz1jheGvnMqjiqC4PFoOvUVahF04zigzm", active: true, onaddtrack: null, onremovetrack: null, onactive: null, …}
about to return

它确实会暂停 getLocalStream 的执行。但它不会暂停其调用者(reducer)的执行 - 它只是向其返回一个 Promise。 - Bergi
也许这个答案可以解释为什么ES使用回调函数、Promise和async await语法。小剧透:在async函数之外,await实际上并不等待任何东西,而async函数会立即返回一个Promise。 - HMR
3个回答

2
使用 redux-thunk。由于你的 reducer 必须返回一个新状态,因此你不能在 reducer 中进行异步操作。
你可以在中间件中执行异步操作,所以你可以编写自己的中间件或使用 redux-thunk。
如果你派发的 action 是一个函数,那么 thunk 将使用 dispatch 和 getState 函数执行该函数。
//create send call action
const createSendCall = id => 
  async dispatch => {
    const localStream =  await getLocalStream();
    console.log('reducer localStream', localStream);
    const call = state.peer.call(id, localStream);
    dispatch(createDoneSend(call));
  }
//create done send action
const createDoneSend = call => ({
  type:"DONE_SEND",
  call
})

//reducer, SEND_CALL never comes here because it's handled in middle ware
switch (action.type) {
  case 'DONE_SEND': {
    return { ...state, call:action.call };
  }
  default: 
    return state;

Thunk可以简化需要分发多个操作的异步中间件或动作。
redux的工作原理以及中间件的工作方式在这里有解释,它将reducer称为“应用程序处理函数”,因为我将其教授为事件存储,而不是特定于redux。
redux不接受状态承诺并在解决时仅设置状态的原因可以在这里找到。当挂起的筛选请求被用户的新请求替换时,挂起的筛选将被拒绝,这就是应该发生的事情,但redux如何知道承诺应该被拒绝?因此,如果我们必须解决问题,那么如何告诉redux在旧的挂起请求解决时不设置状态?这些问题基本上可以通过reducer不返回promises并在中间件中处理来解决。

感谢您的回答。我正在使用peerjs来处理通过WebRTC进行P2P视频聊天的项目。我试图将有关连接和视频通话的信息保存在redux存储中,以便我可以在应用程序的不同组件中访问它。似乎peerjs API严格基于回调(没有Promise API),在这种情况下我仍然能够使用redux-thunk吗? - currenthandle
@Casey thunk 不关心 promises 或 callbacks。你将一个函数作为 action 分发,thunk 将使用 dispatchgetState 运行该函数。在函数中做什么由你决定,可以是 callbacks 或 promises,没有异步代码但可以分发多个 actions,也可以什么都不做,具体取决于该 action 的情况。这个答案有一些代码 apiAsPromise,它将回调类型的函数转换为返回 promise 的函数。这个答案 也是如此,包括上下文对象(this 的值)。 - HMR

1

在reducer中,您需要await调用async函数。

目前,您会调用它,但从未等待其完成。


但我正在等待它在 getLocalStream 中解决。因此,它应该等待承诺解决为常规原始值,然后将该原始值分配给 const localStream,然后继续返回本地流,这是一个原始值。此外,我可以在 reducer 中等待,因为它不是异步函数,将其变成异步函数会破坏应用程序,因为 store 构建需要同步进行。 - currenthandle
Reducer 返回的将是新状态,您现在将新状态设置为状态的 Promise,而不是将其设置为状态。 - HMR
@Casey:你无法逃避异步操作。使用await可以使你的函数变成异步函数并返回一个Promise。 - SLaks

1

您需要在您的reducer函数中也使用async/await,否则getLocalStream()将只返回一个promise实例。

这是因为根据文档

当调用异步函数时,它会返回一个Promise。当异步函数返回一个值时,Promise将被解析为返回的值。当异步函数抛出异常或某个值时,Promise将被拒绝并返回抛出的值。

异步函数可以包含一个等待表达式(await expression),它暂停异步函数的执行并等待传递的Promise解析,然后恢复异步函数的执行并返回已解析的值。

const reducer = async (state = {}, action) => { //HERE
  switch (action.type) {
    case 'SEND_CALL': {
      const { id } = action;
      const localStream =  await getLocalStream(); //HERE
      console.log('reducer localStream', localStream);
      const call = state.peer.call(id, localStream);
      return { ...state, call };
    }
    default: 
      return state;
  }
};

getLocalStream 为什么应该返回一个 Promise?它应该等待 getUserMedia 返回的 Promise 解决后再返回吗? - currenthandle
async functions always wrap the result in a Promise.resolve - Ayush Gupta
Reducer 返回的将是新状态,您现在将新状态设置为状态的 Promise,而不是将其设置为状态。 - HMR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接