2017-05-19 41 views
0

我與下面的目標是:使用承諾到foreach循環內推遲延續

  • 捕捉
  • 掃描通過他們每個人的(按順序)的分辨率列表找到的第一個結果在一個成功的流

爲了驗證這一點,我有testVideoPresence

var testCounter = 0; 
function testVideoPresence(videoElement) { 
    testCounter++; 
    if (testCounter >= 5) { 
     testCounter = 0; 
     return false; 
    } 
    if (!videoElement.videoWidth || videoElement.videoWidth < 10) { // check to prevent 2x2 issue 
     setTimeout(function() { 
      testVideoPresence(videoElement); // try again 
     }, 500); 
    } else if (video.videoWidth * video.videoHeight > 0) { 
     return true; 
    } 
} 

如您所見,我使用setTimeout最多5次遞歸。這就是事情變得棘手:

resolutionTestBuilder.buildTests().then(function (resolutionTests) { 
     // at this point, I have a set of resolutions that I want to try 
     resolutionTests.forEach(function (resolutionTest) { 
      // then I want to iterate over all of them until I find one that works 
      performTest(resolutionTest).then(function (result) { 
       video.srcObject = result.mediaStream; // start streaming to dom 
       if (testVideoPresence(video)) { // here is the pain point - how do I await the result of this within the forEach? 
        // return the dimensions 
       } else { 
        // continue scanning 
       } 
      }).catch(function (error) { 
       logger.internalLog(error); 
      }); 

      // wait to continue until we have our result 

     }); 
    }).catch(function (error) { 
     logger.internalLog(error); 
    }); 

function performTest(currentTest) { 
     return streamHelper.openStream(currentTest.device, currentTest.resolution).then(function(streamData) { 
      return streamData; 
     }).catch(function (error) { 
      logger.internalLog(error); 
     });; 
    }; 

streamHelper.openStream = function (device, resolution) { 
    var constraints = createVideoConstraints(device, resolution); 
    logger.internalLog("openStream:" + resolution.label + ": " + resolution.width + "x" + resolution.height); 
    return navigator.mediaDevices.getUserMedia(constraints) 
     .then(function (mediaStream) { 
      streamHelper.activeStream = mediaStream; 
      return { stream: mediaStream, resolution: resolution, constraints: constraints }; 
      // video.srcObject = mediaStream; // push mediaStream into target element. This triggers doScan. 
     }) 
     .catch(function (error) { 
      if (error.name == "NotAllowedError") { 
       return error.name; 
      } else { 
       return error; 
      } 
     }); 
}; 

我試圖通過決議陣列在繼續之前forEach內等待結果。我知道我可以使用一些先進的技術,如異步/等待,如果我想要transpile - 但我現在堅持香草JS和承諾/ bluebird.js。我有什麼選擇?免責聲明 - 我對承諾不熟悉,因此上述代碼可能非常不正確。

更新:

測試是按重要性順序定義 - 這樣我就需要resolutionTests[0]resolutionTests[1]之前解決。

+1

現在的問題是:您是否想要返回維度的resolutionTests的第一個,或者如果任何可以工作的resolutionTests返回維度,那麼這個維度是否可以,無所謂。後者更容易實現,因爲你可以使用'map'和'Promise.race'。前者有點難度,但你可能會使用'reduce'。在任何情況下,'forEach'都不適用 –

+0

@OvidiuDolha我想要第一個可用於返回尺寸的resolutionTests - 它們從高到低排序,我正在尋找最大兼容分辨率。 – SB2055

回答

1

如果審判順序不重要,您可以簡單地使用mapPromise.race的組合來確保解析列表的第一個承諾解決整個列表。您還需要確保您的承諾在then之內返回其他承諾。

resolutionTestBuilder.buildTests().then(function (resolutionTests) { 
    return Promise.race(resolutionTests.map(function (resolutionTest) { 
     return performTest(resolutionTest).then(function (result) { 
      video.srcObject = result.mediaStream; // start streaming to dom 
      return testVideoPresence(video); 
     }).catch(function (error) { 
      logger.internalLog(error); 
     }); 
    })); 
}).catch(function (error) { 
    logger.internalLog(error); 
}); 

這當然假設testVideoPresence在尺寸不可用時無法解析。

如果審判的順序很重要,那麼reduce的方法可能會奏效。

這將基本上導致承諾的順序應用和由此產生的承諾,直到所有這些承諾解決。

然而,一旦找到解決方法,我們將其連接到集電極的減少,使得進一步的試驗只是返回以及避免進一步的測試(因爲當時這是發現的鏈條已經被註冊)

return resolutionTests.reduce(function(result, resolutionTest) { 
    var nextPromise = result.intermPromise.then(function() { 
     if (result.found) { // result will contain found whenver the first promise that resolves finds this 
      return Promise.resolve(result.found); // this simply makes sure that the promises registered after a result found will return it as well 
     } else { 
      return performTest(resolutionTest).then(function (result) { 
       video.srcObject = result.mediaStream; // start streaming to dom 
       return testVideoPresence(video).then(function(something) { 
        result.found = something; 
        return result.found; 
       }); 
      }).catch(function (error) { 
       logger.internalLog(error); 
      }); 
     } 
    ); 
    return { intermPromise: nextPromise, found: result.found }; 
}, { intermPromise: Promise.resolve() }); // start reduce with a result with no 'found' and a unit Promise 
+0

謝謝 - 雖然順序很重要。看起來你在寫這篇文章時回答了你的評論 - 儘管這對未來的讀者可能有用。 – SB2055

+0

增加了另一種方法作爲一個想法 - 它可能包含一些錯誤,但想法是你減少嘗試列表 - 這意味着所有的承諾將被鏈接在一起 - 然後在第一個你找到一些你確保未來的承諾不再嘗試 –

+0

我得到語法錯誤'=>' - 有沒有辦法使用這與純JS? – SB2055

1

首先,您的testVideoPresence返回未定義。它不會那樣工作。可以這樣做:

function testVideoPresence(videoElement,callback,counter=0) { 
if(counter>10) callback(false); 
    if (!videoElement.videoWidth || videoElement.videoWidth < 10) { 
     setTimeout(testVideoPresence, 500,videoElement,callback,counter+1); 
    } else if (video.videoWidth * video.videoHeight > 0) { 
     callback(true); 
    } 
} 

所以你可以做:

testVideoPresence(el, console.log); 

我們在foreach。您無法以任何方式讓出forEach。然而,你可以寫你自己的遞歸的forEach:

(function forEach(el,index) { 
    if(index>=el.length) return false; 

    performTest(el[index]).then(function (result) { 
     video.srcObject = result.mediaStream; // start streaming to dom 

     testVideoPresence(video,function(success){ 
      if(!success) return alert("NOO!"); 
      //do sth 
      //proceed 
      setTimeout(forEach,0,el,index+1); 
     }); 
    }).catch(function (error) { 
      logger.internalLog(error); 
}); 
})(resolutionTests,0);//start with our element at index 0 
0

function raceSequential(fns) { 
 
    if(!fns.length) { 
 
     return Promise.resolve(); 
 
    } 
 
    return fns.slice(1) 
 
     .reduce(function(p, c) { 
 
      return p.catch(c); 
 
     }, fns[0]()); 
 
} 
 

 
// "Resolution tests" 
 
var t1 = function() { return new Promise(function(_, reject) { setTimeout(() => reject('t1'), 1000); })}; 
 
var t2 = function() { return new Promise(function(resolve) { setTimeout(() => resolve('t2'), 1000); })}; 
 
var t3 = function() { return new Promise(function(resolve) { setTimeout(() => resolve('t3'), 1000); })}; 
 

 
var prom = raceSequential([t1, t2, t3]) 
 
    .then(function(result) { console.log('first successful result: ' + result); });

掃描代碼,則表示有其他異步有關的問題。

+0

我應該指定這個 - 但順序很重要。如果第一次測試有效,我更喜歡測試數組中的任何下列項目。 – SB2055

+0

你想依次運行它們還是繼續嘗試並行化? – Ben

+0

我需要它們按順序運行。 – SB2055