2017-05-19 3 views
0

Mein Ziel mit der unten ist:Promises Mit Fortsetzung innerhalb einer foreach-Schleife verschieben

  • Aufnahme eine Liste von Auflösungen
  • Scan durch jeden von ihnen (in der Reihenfolge) die ersten zu finden, dass die Ergebnisse in einem erfolgreichen Strom

Um dies zu testen, ich habe 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; 
    } 
} 

Wie Sie sehen können, verwende ich eine setTimeout, um höchstens 5 mal zu rekrutieren. Dies ist, wo es kompliziert:

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

Ich versuche im forEach für das Ergebnis zu warten, bevor sie durch die Anordnung von Resolutionen fortzusetzen. Ich weiß, dass ich einige fortgeschrittene Techniken wie async/have verwenden kann, wenn ich transpilieren möchte - aber ich stecke fest mit Vanille JS und Versprechungen/bluebird.js für jetzt. Was sind meine Möglichkeiten? Haftungsausschluss - Ich bin neu in Versprechungen, so dass der oben genannte Code sehr falsch sein könnte.

Update:

Tests in der Reihenfolge ihrer Bedeutung definiert sind - so dass ich resolutionTests[0] brauchen vor resolutionTests[1] zu lösen.

+1

die Frage ist: wollen Sie die erste der resolutionTests, die funktioniert, um die Dimensionen zurückgeben, oder ist es in Ordnung, wenn 'any 'der resolutionTests, die Arbeit die Dimensionen zurückgibt - egal welche. Letzteres ist einfacher zu implementieren, da Sie 'map' und' Promise.race' verwenden können. Ersteres ist etwas schwieriger, aber Sie würden wahrscheinlich 'reduce' verwenden. In keinem Fall ist 'forEach' geeignet –

+0

@OvidiuDolha Ich möchte den ersten der resolutionTests, der funktioniert, um die Dimensionen zurückzugeben - sie sind von hoch nach niedrig geordnet und ich suche nach der maximal kompatiblen Auflösung. – SB2055

Antwort

1

Wenn die Reihenfolge der Versuche nicht wichtig ist, können Sie einfach map kombiniert mit Promise.race verwenden, um sicherzustellen, dass das erste Versprechen einer Liste, die löst, die gesamte Liste auflöst. Sie müssen auch sicherstellen, dass Ihre Versprechen andere Versprechen innerhalb then zurückgeben.

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

Dies setzt natürlich voraus, dass testVideoPresence lösen sich nicht, wenn Sie die Abmessungen nicht verfügbar sind.

Wenn die Reihenfolge der Testversion wichtig ist, könnte ein reduce Ansatz funktionieren.

Dies wird im Grunde zu einer sequenziellen Anwendung der Versprechen und der daraus resultierenden Versprechen führen, bis alle von ihnen gelöst sind.

Sobald jedoch die Lösung gefunden wird, legen wir es an den Kollektor des reduzieren, so dass weitere Studien zurückkehren einfach, dass auch und vermeiden weitere Tests (weil durch die Zeit dies die Kette gefunden wird ist bereits registriert)

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

Danke - obwohl die Reihenfolge wichtig ist. Sieht so aus, als ob ich deinen Kommentar beantwortet habe, als du das geschrieben hast - obwohl dies für zukünftige Leser nützlich sein könnte. – SB2055

+0

Der andere Ansatz als eine Idee hinzugefügt - es könnte einige Fehler enthalten, aber die Idee ist, reduzieren Sie die Liste der Versuche - was bedeutet, dass alle Versprechen zusammen verkettet werden - dann auf der ersten finden Sie etwas, Sie sicherstellen, dass die zukünftigen sind nicht mehr versuchen –

+0

Ich bekomme Syntaxfehler auf '=>' - gibt es eine Möglichkeit, dies mit plain JS zu verwenden? – SB2055

1

Zuerst gibt Ihre testVideoPresence undefined zurück. Es wird nicht so funktionieren. tun kann:

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

SO können Sie tun:

testVideoPresence(el, console.log); 

Nun zum forEach. Sie können das forEach in keiner Weise liefern. Allerdings könnten Sie Ihre eigene rekursive forEach schreiben:

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

Code Scannen zeigt Ihnen andere Asynchron-bezogene Probleme.

+0

Ich hätte das spezifizieren sollen - aber die Reihenfolge ist wichtig. Wenn der erste Test funktioniert, bevorzuge ich ihn gegenüber den folgenden Punkten im Testfeld. – SB2055

+0

Möchten Sie sie nacheinander ausführen oder die Parallelisierung fortsetzen? – Ben

+0

Ich brauche sie nacheinander zu laufen. – SB2055