【问题标题】:How to fix promises i̶n̶ ̶F̶i̶r̶e̶f̶o̶x̶?如何修复承诺 i̶n̶̶F̶i̶r̶e̶f̶o̶x̶?
【发布时间】:2017-06-25 18:40:27
【问题描述】:

这是我的 Promise 代码的简化示例:

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);
    }
  });
}

我在几个地方异步使用这个函数。 这个问题与函数至少被调用两次有关,但在第二次调用中,promise 永远不会被解决/拒绝。 此代码在 Chrome 中完美运行。我也尝试使用 Angular promises service $q,但也没有用。

我做错了什么以及如何使这段代码工作?

另外,我一直在思考如何在这种情况下避免承诺,但我别无选择,因为当用户确认麦克风和摄像头访问请求时,我不得不等待。

更新:

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

【问题讨论】:

  • 这是一个promise anti-pattern,因为您将现有的承诺包装在另一个承诺中,而不是仅仅返回您已经拥有的承诺。在你的if(),你可以只做return Promise.resolve(sharedLocalStream),在你的else,你可以只返回你已经拥有的承诺而无需创建新的承诺。
  • 在 Firefox 中没有破坏承诺 - 问题出在其他地方
  • 我们不清楚您的问题到底是什么,但我怀疑这是 Firefox 中的承诺问题。这更有可能是您的代码如何使用 Promise 或您调用的某些函数在您调用它两次时如何响应的问题。
  • @jfriend00 你能详细说明一下吗?我刚刚阅读了这个stackoverflow.com/questions/23803743/…,但正如您所见,我的代码需要对刚刚获得的流进行一些处理。

标签: javascript angularjs promise es6-promise


【解决方案1】:

如果getVideoStream() 被调用两次,您的代码就会出现并发问题。因为在第一次调用 sharedLocalStream 变量之前第二次调用 getVideoStream() 时没有强制排队或排序,所以您很容易遇到这样一种情况,即在两个调用开始之前创建两个流sharedLocalStream 有一个值。

这是代码设计的问题,与运行它的平台无关。解决这个问题的常用方法是将第一个操作的承诺存储到共享变量中。然后你测试看看那里是否已经有一个承诺。如果有,您只需返回该承诺。

如果你缓存 promise 而不是流,你可以这样做:

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;
}

第一次调用会将sharedLocalStreamPromise 初始化为一个promise。当第二个调用(或任何后续调用)进来时,它只会返回相同的承诺。

我正在考虑这段代码的一个极端情况。如果 Promise 被拒绝并且已经发生了对同一函数的第二次调用,它也会有被​​拒绝的 Promise。

【讨论】:

  • 看来我必须实现排队并等到用户确认请求。有什么好的方法吗?
  • 我喜欢你的解决方案,我想存储承诺,但最终存储了一个流,这是一个坏主意。
  • clear the promise so we don't cache a rejected promise 在这种情况下是没有意义的,因为一旦用户拒绝 getUserMedia,所有后续调用 getUserMedia 都不会再次提示用户,只是返回一个被拒绝的 promise
  • (不必要的)全局变量很糟糕,顺便说一句:p
  • @JaromandaX - 如果 OP 需要,它可以包装在 IIFE 中。如果这是一个与他人共享的库,绝对会这样做。如果只是您自己的项目,则更多可选。
【解决方案2】:

没有 IIFE

let sharedLocalStreamPromise;

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

与 IIFE

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

或者

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

这最后一个在第一次被调用时创建一个result(由getUserMedia返回的promise),然后覆盖自身以返回该结果......在随后的getVideoStream调用中没有条件 - 所以,理论上“更快”(在这种情况下,速度是一个有争议的问题)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-25
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-30
    • 1970-01-01
    相关资源
    最近更新 更多