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.
我在几个地方异步地使用这个函数。这个问题与函数至少被调用两次有关,但是在第二次调用中,这个函数承诺永远不会被解决/拒绝。这段代码在Chrome中运行良好。我也尝试使用$q的角承诺服务,但它也不起作用。
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.
同时,我也在思考如何在这种情况下避免承诺,我别无选择,因为当用户确认mic&camera访问请求时,我不得不等待。
Update:
更新:
var constraints = {
audio: true,
video: true
};
2 个解决方案
#1
0
Without IIFE
没有IIFE
let sharedLocalStreamPromise;
function getVideoStream() {
return sharedLocalStreamPromise = sharedLocalStreamPromise || navigator.mediaDevices.getUserMedia(constraints);
};
with IIFE
与IIFE
const getVideoStream = (() => {
let sharedLocalStreamPromise;
return () => sharedLocalStreamPromise = sharedLocalStreamPromise || navigator.mediaDevices.getUserMedia(constraints);
})();
Alterntively
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)
最后一个在第一次调用时创建一个结果(即getUserMedia返回的承诺),然后重写自身以返回结果……在以后调用getVideoStream时没有条件——因此,从理论上说,“更快”(在本例中,速度是一个没有意义的点)
#2
2
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.
如果调用两次getVideoStream(),则代码会有并发问题。由于在第一次调用getVideoStream()之前第二次调用它并更新sharedLocalStream变量之前没有强制排队或排序,因此很容易出现这样的情况:在启动两个调用之前,在sharedLocalStream有一个值。
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.
第一个调用将初始化sharedLocalStreamPromise以实现一个承诺。当第二个调用(或任何后续调用)进来时,它将返回相同的承诺。
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.
这段代码有一个边界情况我正在考虑。如果这个承诺被拒绝,对同一个函数的第二个调用已经发生,它也会有拒绝的承诺。
#1
0
Without IIFE
没有IIFE
let sharedLocalStreamPromise;
function getVideoStream() {
return sharedLocalStreamPromise = sharedLocalStreamPromise || navigator.mediaDevices.getUserMedia(constraints);
};
with IIFE
与IIFE
const getVideoStream = (() => {
let sharedLocalStreamPromise;
return () => sharedLocalStreamPromise = sharedLocalStreamPromise || navigator.mediaDevices.getUserMedia(constraints);
})();
Alterntively
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)
最后一个在第一次调用时创建一个结果(即getUserMedia返回的承诺),然后重写自身以返回结果……在以后调用getVideoStream时没有条件——因此,从理论上说,“更快”(在本例中,速度是一个没有意义的点)
#2
2
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.
如果调用两次getVideoStream(),则代码会有并发问题。由于在第一次调用getVideoStream()之前第二次调用它并更新sharedLocalStream变量之前没有强制排队或排序,因此很容易出现这样的情况:在启动两个调用之前,在sharedLocalStream有一个值。
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.
第一个调用将初始化sharedLocalStreamPromise以实现一个承诺。当第二个调用(或任何后续调用)进来时,它将返回相同的承诺。
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.
这段代码有一个边界情况我正在考虑。如果这个承诺被拒绝,对同一个函数的第二个调用已经发生,它也会有拒绝的承诺。