2016-01-07 10 views
9

Wie kann ich eine Kette von Versprechen verzögern? Ich brauche das, weil ich auf eine CSS-Animation warten möchte, bevor ich mit dem Skript fortfahre.Wie verzögert man ein Versprechen in der Funktion

Zweck der Funktion ist das Öffnen einer Ansicht. Wenn die Ansicht nicht bereits geöffnet ist, öffnen Sie sie (indem Sie eine Klasse ändern), warten Sie auf die CSS-Animation und fahren Sie fort. Wenn die Ansicht bereits geöffnet ist, tun Sie nichts und fahren Sie fort.

Ich möchte die Funktion wie folgt nennen: (es eine Funktion in einem Winkelregler)

$scope.openView(viewId).then(function() {    
    $scope.openAnotherView(anotherViewId); 
}); 


/** Function to open some view **/ 
$scope.openView = function (viewId) { 
    function timeout(delay) { 
     return new Promise(function(resolve, reject) { 
      $timeout(resolve, delay); 
     }); 
    } 

    // Check if view is already open 
    if ($scope.viewId != viewId) { 
     $scope.viewId = viewId;    

     // get data from ajaxcall (also a promise) 
     return MyService.getData(viewId).then(function(data) { 
      // add data to view 
      // change class to open view 
      // this is working ok! 
     }).then(function() { 
      return timeout(30000 /* some large number (testing purpose) */) 
     }); 
    } else { 
     // view is already open, so return here we don't have to wait 
     // return empty promise, with no timeout 
     return new Promise(function(resolve, reject) { 
      resolve() 
     });  
    } 
} 

Dieser Code funktioniert, aber die Verzögerung funktioniert nicht. Ist meine Vorgehensweise in Ordnung? Was fehlt mir hier?


Edit 1: verbessert den Code mit dem Vorschlag von @sdgluck


Edit 2: Einige Klärung der Hauptfrage:

die Hauptfrage eine Klarstellung etwas mehr: Kann ich diese Konstruktion in meinem Code verwenden?

// code doesnt know wheter to wait or not 
// can the Promise do this? 
openView().then(function() {    
    openAnotherView(); 
} 

Ergebnis 1:

der Browser openView() nennen, aber da es bereits es öffnet openAnotherView() nur sofort anrufen (keine Verzögerung).

Ergebnis 2:

Die Ansicht nicht geöffnet ist, openView() wird es so öffnen, dann eine Verzögerung (? Oder als @Dominic Tobias weist darauf hin, eine eventlister hinzufügen), dann openAnotherView() nach einer gewissen Verzögerung nennen.

Danke für jede Hilfe!

bearbeiten 3: eine Geige mit dem Problem Hinzugefügt erklärt http://jsfiddle.net/C3TVg/60/

+1

Ich denke, Sie können dies in die falsche Richtung gehen. Es klingt, als ob Sie möchten, dass die Animation wie [this] (https://davidwalsh.name/css-animation-callback) abgeschlossen wird. –

Antwort

1

Wie kann ich eine Kette von Versprechen verzögern?

$timeout gibt ein Versprechen. Zurückgeben das Versprechen zu Kette es.

$scope.openView = function (viewId) { 
    // Check if view is already open 
    if ($scope.viewId == viewId) { 
     //chain right away with empty promise 
     return $q.when(); 
    }; 

    //otherwise if view is not already open 

    var p = MyService.getData(viewId).then(function(data) { 
      // add data to view 
      // change class to open view 
      // this is working ok! 
    }); 

    var pDelayed = p.then (function() { 
     //return to chain delay 
     return $timeout(angular.noop, 30000); 
     }); 

    //return delayed promise for chaining 
    return pDelayed; 
}; 

$scope.openView(viewId).then(function() { 
    //use chained promise    
    $scope.openAnotherView(anotherViewId); 
}); 
+0

Danke für deine Antwort, ich habe gerade meine Frage aktualisiert. Ich habe versucht zu verdeutlichen, dass ich jetzt nicht vorher auflege, wenn ich warten muss. – 11mb

+0

Aktualisiert, um bedingte Verzögerung hinzuzufügen. – georgeawg

3

Jede then accepts a function, die ein Versprechen zurückkehren. Es akzeptiert keine Instanz von Promise. Sie möchten den Anruf zu timeout zurückzukehren:

return MyService 
    .getData(viewId) 
    .then(function(data) { 
     // ... 
    }) 
    .then(function() { 
     return timeout(3000); 
    }); 

Alternativ haben timeout return eine Funktion statt eines Versprechen:

function timeout(delay) { 
    return function() { 
     return new Promise(function(resolve, reject) { 
      //      ^^^^^^^ (misspelt in your example) 
      $timeout(resolve, delay); 
     }); 
    }; 
} 

Und dann kann man es wie in Ihrem Beispiel verwenden:

return MyService 
    .getData(viewId) 
    .then(function(data) { 
     // ... 
    }) 
    .then(timeout(3000)); 
+0

Thx für die Identifizierung meines Fehlers mit der Timout-Funktion und aufklären, wie man es löst. Aber ich denke, dass es ein anderes Problem mit meinem Code gibt, wie @Dominic Tobias darauf hinweist. – 11mb

+1

@ 11mb Kein Problem. Und ja, die andere Antwort ist umfassender. :-) – sdgluck

13

Um eine Verspätung zu verzögern, rufen Sie einfach die Funktion resolve nach einer Wartezeit auf.

new Promise(function(resolve, reject) { 
    setTimeout(function() { 
    resolve(); 
    }, 3000); // Wait 3s then resolve. 
}); 

Das Problem mit dem Code ist, dass Sie ein Versprechen und dann innerhalb der then dieses Versprechen kehren Sie ein anderes schaffen und die Urverheißung darauf zu warten erwarten - ich bin das ist Angst nicht, wie verspricht Arbeit. Sie müssten in der Versprechen Funktion alle Ihre Warte tun und dann Entschlossenheit nennen:

Edit: Das ist nicht wahr, Sie können die Versprechen Kette in jedem then verzögern:

function promise1() { 
 
    return new Promise((resolve) => { 
 
    setTimeout(() => { 
 
     console.log('promise1'); 
 
     resolve(); 
 
    }, 1000); 
 
    }) 
 
    .then(promise2); 
 
} 
 

 
function promise2() { 
 
    return new Promise((resolve) => { 
 
    setTimeout(() => { 
 
     console.log('promise2'); 
 
     resolve(); 
 
    }, 1000); 
 
    }); 
 
} 
 

 
function promise3() { 
 
    return new Promise((resolve) => { 
 
    setTimeout(() => { 
 
     console.log('promise3'); 
 
     resolve(); 
 
    }, 1000); 
 
    }); 
 
} 
 

 
promise1() 
 
    .then(promise3) 
 
    .then(() => { 
 
    console.log('...finished'); 
 
    })

Das ist jedoch keine gute Möglichkeit, auf eine CSS-Animation zu warten. Es ist besser, die transitionend Veranstaltung zu hören:

element.addEventListener('transitionend', onTransitionEnd); 
element.classList.add('transition-me'); 

Hinweis, wenn Sie ein animation anstelle ein transition das gleiche Konzept verwenden gilt aber das animationend Ereignis verwenden.

+0

Die Verwendung von 'setTimeout' über' $ timeout' löst das Problem'delay is not working 'nicht. Aber gute Ratschläge, wie man auf Übergänge wartet. :-) – sdgluck

+0

Ah, ich mochte die Animation/Übergangszeit in der CSS und JS nicht. Ich werde mich definitiv darum kümmern! Über Ihren Vorschlag: wenn ich ein Zeitlimit um die "Daten hinzufügen zu sehen" -Funktion hinzufügen und jetzt wartet es vor dem Öffnen meiner Ansicht, aber es verzögert nicht den Aufruf des ersten dann ("Alles ist bereit") und der zweite dann ('openAnotherView'). Ich nannte Entschlossenheit wie folgt: Promise.resolve(); – 11mb

+0

Sie möchten also ein Versprechen abgeben, aber Sie wollen nicht warten? –

Verwandte Themen