It appears that my use of await
is not behaving as I understood it to.
I thought that an async
function and the await
keyword could be used on a call that returns a promise (for instance navigator.mediaDevices.getUserMedia
) and it would pause the function execution (like a generator function) until the promise resolved and then it would continue on with the function.
Even though I am awaiting the call it's returning right away and my console logs are happening out of order. Specifically the console log starting with "reducer" appears in the console before the one starting with "getter", when it should be the other way around/is the opposite of how (I thought) the stack trace should run.
Can anyone tell me what's going on here?
Async helper function 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;
}
};
Console:
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
Use redx-thunk. Since your reducer has to return a new state you cannot do asynchronous operations in the reducer.
You can do asynchronous operations in middle ware so you can either write middle ware yourself or use redux-thunk.
If you dispatch an action that is a function then thunk will execute that function with the dispatch
and a getState
function.
//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 will simplify asynchronous middle ware or actions that need to dispatch multiple actions.
How redux works and how the middle ware works is explained here it is calling the reducer "application handler function" because I was teaching it as event store, not redux specific.
A reason why redux doesn't accept promises as state and just set the state when it resolves can be found here. When a pending filter request is replaced with a newer request from the user the pending filter will reject, this is what should happen but how would redux know the promise is supposed to reject? So if we have to resolve then how do we tell redux not to set the state when the older pending request resolves? These problems can basically be solved by reducer not returning promises and handle that in the middle ware.
You need to await
the call to your async
function in the reducer.
Right now, you call it, but never wait for it to finish.
You need to use async/await
in your reducer
function too, otherwise getLocalStream()
will just return a promise
instance.
This is because, according to the docs,
When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.
An async function can contain an await expression, that pauses the execution of the async function and waits for the passed Promise's resolution, and then resumes the async function's execution and returns the resolved value.
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;
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With