2015-05-28 5 views
14

Ich muss ein JavaScript-Versprechen erstellen, das nicht aufgelöst wird, bis eine bestimmte Bedingung erfüllt ist. Nehmen wir an, ich habe eine Bibliothek von Drittanbietern, und ich muss warten, bis eine bestimmte Datenbedingung in dieser Bibliothek existiert.Verwenden Sie Versprechen, um zu warten, bis Abfragebedingung erfüllt ist

Das Szenario, das mich interessiert, ist eines, wo es keine Möglichkeit gibt zu wissen, wann diese Bedingung erfüllt ist, außer durch einfaches Polling.

Ich kann ein Versprechen erstellen, das darauf wartet - und dieser Code funktioniert, aber gibt es einen besseren oder prägnanteren Ansatz für dieses Problem?

function ensureFooIsSet() { 
    return new Promise(function (resolve, reject) { 
     waitForFoo(resolve); 
    }); 
} 

function waitForFoo(resolve) { 
    if (!lib.foo) { 
     setTimeout(waitForFoo.bind(this, resolve), 30); 
    } else { 
     resolve(); 
    } 
} 

Verbrauch:

ensureFooIsSet.then(function(){ 
    ... 
}); 

Ich würde normalerweise eine maximale Abfragezeit implementieren, wollen aber nicht, dass das Problem hier trüben.

+0

Das sieht OK aus. Sie müssen nicht wirklich den externen Bereich mit waitForFoo verschmutzen, aber es ist ein Detail und hängt vom Rest des Codes ab. Verwenden Sie native Versprechungen oder eine bestimmte Bibliothek? –

+0

vereinbart - das ist nur ein Ausschnitt des gesamten Codes. Dies würde einheimische Promise verwenden. Ich habe dieses Muster ein paar Mal kommen lassen und interessiert, ob es einen besseren Weg gibt, das zu strukturieren. –

+0

Ein einfacherer Ansatz wäre Object.observe das Bibliotheksobjekt –

Antwort

24

Eine kleine Variation wäre eine benannte IIFE zu verwenden, so dass der Code ein wenig kürzer und vermeidet die Verschmutzung den Außenbereiches ist:

für dieses Problem
function ensureFooIsSet() { 
    return new Promise(function (resolve, reject) { 
     (function waitForFoo(){ 
      if (lib.foo) return resolve(); 
      setTimeout(waitForFoo, 30); 
     })(); 
    }); 
} 
+0

Ich wollte ein IIFE verwenden, dachte aber nicht darüber nach, es zu benennen. –

2

Gibt es einen prägnanten Ansatz?

Nun, mit dieser Funktion waitForFoo Sie nicht eine anonyme Funktion in Ihrem Konstruktor brauchen überhaupt:

function ensureFooIsSet() { 
    return new Promise(waitForFoo); 
} 

den Umfang zu vermeiden, umweltfreundlich, würde ich empfehlen, entweder beide zu wickeln in einem IIFE oder waitForFoo die Funktion innerhalb des ensureFooIsSet Umfang zu bewegen:

function ensureFooIsSet(timeout) { 
    var start = Date.now(); 
    return new Promise(waitForFoo); 
    function waitForFoo(resolve, reject) { 
     if (window.lib && window.lib.foo) 
      resolve(window.lib.foo); 
     else if (timeout && (Date.now() - start) >= timeout) 
      reject(new Error("timeout")); 
     else 
      setTimeout(waitForFoo.bind(this, resolve, reject), 30); 
    } 
} 

Alternativ wird die Bindung zu vermeiden, was benötigt passieren rund resolve und reject Sie könnten es in den Promise Konstruktor Rückruf wie @ DenysSéguret vorgeschlagen verschieben.

Gibt es einen besseren Ansatz?

Wie @BenjaminGruenbaum kommentierte, könnten Sie die .foo Eigenschaft überwachen, die zugewiesen werden soll, z. mit einem Setzer:

function waitFor(obj, prop, timeout, expected) { 
    if (!obj) return Promise.reject(new TypeError("waitFor expects an object")); 
    if (!expected) expected = Boolean; 
    var value = obj[prop]; 
    if (expected(value)) return Promise.resolve(value); 
    return new Promise(function(resolve, reject) { 
     if (timeout) 
      timeout = setTimeout(function() { 
       Object.defineProperty(obj, prop, {value: value, writable:true}); 
       reject(new Error("waitFor timed out")); 
      }, timeout); 
     Object.defineProperty(obj, prop, { 
      enumerable: true, 
      configurable: true, 
      get: function() { return value; }, 
      set: function(v) { 
       if (expected(v)) { 
        if (timeout) cancelTimeout(timeout); 
        Object.defineProperty(obj, prop, {value: v, writable:true}); 
        resolve(v); 
       } else { 
        value = v; 
       } 
      } 
     }); 
    }); 
    // could be shortened a bit using "native" .finally and .timeout Promise methods 
} 

Sie können es wie waitFor(lib, "foo", 5000) verwenden.

0
function getReportURL(reportID) { 
    return() => viewReportsStatus(reportID) 
    .then(res => JSON.parse(res.body).d.url); 
} 

function pollForUrl(pollFnThatReturnsAPromise, target) { 
    if (target) return P.resolve(target); 
    return pollFnThatReturnsAPromise().then(someOrNone => pollForUrl(pollFnThatReturnsAPromise, someOrNone)); 
} 

pollForUrl(getReportURL(id), null); 
Verwandte Themen