zoonman
zoonman

Reputation: 1163

How to fix promises i̶n̶ ̶F̶i̶r̶e̶f̶o̶x̶?

Here is a simplified sample of my Promise code:

var sharedLocalStream = null;
// ...
function getVideoStream() {
  return new Promise(function(resolve, reject) {
    if (sharedLocalStream) {
      console.log('sharedLocalStream is defined');
      resolve(sharedLocalStream);
    } else {
      console.log('sharedLocalStream is null, requesting it');
      navigator
          .mediaDevices
          .getUserMedia(constraints)
          .then(function(stream) {
            console.log('got local videostream', stream);
            sharedLocalStream = stream;
            resolve(sharedLocalStream);
          })
          .catch(reject);
    }
  });
}

I'm using this function asynchronously in several places. The issue is related to the fact that function gets called at least twice, but in a second call promise never gets resolved/rejected. This code works perfectly in Chrome. Also I tried to use Angular promises service $q, but it didn't work either.

What am I doing wrong and how to make this code work?

Also, I was thinking a lot about ways how I can avoid promises in this case and I have no choice because I forced to wait when user confirms mic&camera access request.

Update:

var constraints = {
    audio: true,
    video: true
  };

Upvotes: 0

Views: 93

Answers (2)

Jaromanda X
Jaromanda X

Reputation: 1

Without IIFE

let sharedLocalStreamPromise;

function getVideoStream() {
    return sharedLocalStreamPromise = sharedLocalStreamPromise || navigator.mediaDevices.getUserMedia(constraints);
};

with IIFE

const getVideoStream = (() => {
    let sharedLocalStreamPromise;
    return () => sharedLocalStreamPromise = sharedLocalStreamPromise || navigator.mediaDevices.getUserMedia(constraints);
})();

Alterntively

var getVideoStream = () => {
    const result = navigator.mediaDevices.getUserMedia(constraints);
    getVideoStream = () => result;
    return getVideoStream();
};

this last one creates a result (being the promise returned by getUserMedia) the first time it is called, and then overwrites itself to just return that result ... no conditionals in subsequent invocations of getVideoStream - so, theoretically "faster" (in this case, speed is a moot point though)

Upvotes: 0

jfriend00
jfriend00

Reputation: 707238

Your code has concurrency issues if getVideoStream() is called twice. Because there is no forced queuing or sequencing when getVideoStream() is called a second time before the first call to it has updated the sharedLocalStream variable, you can easily end up with a situation where you create two streams before both calls are started before sharedLocalStream has a value.

This is an issue with the design of the code, not with the platform you are running it on. The usual way around this is to store the promise from the first operation into the shared variable. You then test to see if there's a promise already in there. If there is, you just return that promise.

If you cache the promise instead of the stream, you can do it as simply as this:

var sharedLocalStreamPromise = null;
function getVideoStream() {
    // if we've already requested a local stream, 
    // return the promise who's fulfilled value is that stream
    if (!sharedLocalStreamPromise) {
        sharedLocalStreamPromise = navigator.mediaDevices.getUserMedia(constraints).catch(function(err) {
            // clear the promise so we don't cache a rejected promise
            sharedLocalStreamPromise = null;
            throw err;
        });
    }
    return sharedLocalStreamPromise;
}

The first call will initialize sharedLocalStreamPromise to a promise. When the second call (or any subsequent call) comes in, it will just return that same promise.

There is an edge case with this code which I'm thinking about. If the promise rejects and a second call to the same function has already occurred, it will also have the promise that rejects.

Upvotes: 2

Related Questions