2013-11-25 11 views
20

Czy istnieje sposób oczekiwania na obietnicę, aby można było uzyskać rzeczywisty wynik i zwrócić ją zamiast zwrócić obietnicę? Myślę o czymś podobnym do tego, jak słowo kluczowe C# czeka na działanie z Tasks.

Oto przykład, dlaczego chciałbym mieć metodę taką jak canAccess(), która zwraca wartość true lub false zamiast obietnicy, aby mogła być użyta w instrukcji if. Metoda canAccess() mogłaby wywołać wywołanie AJAX za pomocą $ http lub $ resource, a następnie jakoś poczekać, aż obietnica zostanie rozwiązana.

będzie wyglądać mniej więcej tak:

$scope.canAccess = function(page) { 
    var resource = $resource('/api/access/:page'); 
    var result = resource.get({page: page}); 

    // how to await this and not return the promise but the real value 
    return result.canAccess; 
} 

Czy mimo to zrobić?

Dzięki.

Odpowiedz

24

W ogóle, że to zły pomysł. Pozwól mi powiedzieć, dlaczego. JavaScript w przeglądarce jest w zasadzie potworem z pojedynczym gwintem. Pomyśl o tym, jest to również pojedynczy wątek w Node.js też. Więc wszystko, co robisz, aby nie "zwracać" w momencie, w którym zaczniesz czekać na zdalną prośbę o sukces lub niepowodzenie, będzie prawdopodobnie wymagało pewnego rodzaju pętli, aby opóźnić wykonanie kodu po żądaniu. Coś takiego:

var semaphore = false; 
var superImportantInfo = null; 

// Make a remote request. 
$http.get('some wonderful URL for a service').then(function (results) { 
    superImportantInfo = results; 

    semaphore = true; 
}); 

while (!semaphore) { 
    // We're just waiting. 
} 

// Code we're trying to avoid running until we know the results of the URL call. 
console.log('The thing I want for lunch is... " + superImportantInfo); 

Ale jeśli spróbujesz, że w przeglądarce, a połączenie trwa zbyt długo, przeglądarka pomyśli kodu JavaScript utknął w pętli i pojawiają się wiadomości w twarz autora dając użytkownik ma szansę zatrzymać swój kod. dlatego JavaScript struktur IT tak:

// Make a remote request. 
$http.get('some wonderful URL for a service').then(function (results) { 
    // Code we're trying to avoid running until we know the results of the URL call. 
    console.log('The thing I want for lunch is... " + results); 
}); 

// Continue on with other code which does not need the super important info or 
// simply end our JavaScript altogether. The code inside the callback will be 
// executed later. 

Pomysł jest, że kod w zwrotnego zostanie wywołany przez zdarzenie ilekroć powraca wzywania serwisu. Ponieważ kierowane zdarzeniami jest to, jak JavaScript je lubi. Timery w JavaScript to zdarzenia, akcje użytkownika to zdarzenia, połączenia HTTP/HTTPS do wysyłania i odbierania danych również generują zdarzenia. Oczekuje się, że skonstruujesz swój kod, aby zareagował na te zdarzenia, gdy nadejdą.

Nie możesz ustrukturyzować swojego kodu w taki sposób, że uważa on, że wartośćA może być fałszywa, dopóki zdalne wywołanie usługi nie powróci i może się okaże, że to prawda? Robię to cały czas w kodzie AngularJS, gdzie nie wiem, jaki jest ostateczny zestaw uprawnień, które powinienem pokazać użytkownikowi, ponieważ jeszcze ich nie otrzymałem lub nie otrzymałem wszystkich danych do wyświetlenia w strona na początku. Mam wartości domyślne, które wyświetlają się, dopóki nie pojawią się prawdziwe dane, a następnie strona dostosuje się do nowego formularza na podstawie nowych danych. Dwukierunkowe wiązanie AngularJS sprawia, że ​​jest to naprawdę łatwe.

+0

Tak, masz kilka bardzo dobrych punktów. Mogę zdecydowanie ukształtować mój kod w ten sposób i nie powinno to być zbyt trudne. Byłem bardziej ciekawy, czy istnieje sposób, aby to zrobić, ale wyperswadowałeś mi z tego pożytku. Podoba mi się pomysł nieuzyskania dostępu, a następnie umożliwienie dwustronnego zajęcia się resztą. Dzięki. –

+5

W pierwszym przykładzie nie sądzę, aby warunek dla stwierdzenia 'while' był zawsze zgodny z prawdą. Dzieje się tak dlatego, że będzie on działał synchronicznie i zablokuje żądanie HTTP od zawsze. – mightyiam

+6

W niektórych przypadkach naprawdę chcesz poczekać, myślę, że to było oryginalne pytanie. Ta odpowiedź tak naprawdę nie odpowiada. –

1

Moje podejście było utworzyć funkcję ze starymi javascript obiekty następująco:

var globalRequestSync = function (pUrl, pVerbo, pCallBack) { 

     httpRequest = new XMLHttpRequest(); 
     httpRequest.onreadystatechange = function() { 

      if (httpRequest.readyState == 4 && httpRequest.status == 200) { 
       pCallBack(httpRequest.responseText); 
      } 
     } 
     httpRequest.open(pVerbo, pUrl, false); 
     httpRequest.send(null); 

    }; 
0

Niedawno miałem ten problem i zrobił narzędzie o nazwie „syncPromises”. To zasadniczo działa, wysyłając to, co nazwałem "listą instrukcji", która będzie tablicą funkcji, które będą wywoływane w kolejności.Będziesz musiał najpierw wywołać pierwszą(), aby kopać rzeczy, dynamicznie dołączyć nową .then(), gdy odpowiedź wróci z następnym elementem na liście instrukcji, więc będziesz musiał śledzić indeks.

// instructionList is array. 
 
function syncPromises (instructionList) { 
 
    var i = 0, 
 
    defer = $q.defer(); 
 

 
    function next(i) { 
 

 
    // Each function in the instructionList needs to return a promise 
 
    instructionList[i].then(function() { 
 
     var test = instructionList[i++]; 
 
     if(test) { 
 
     next(i); 
 
     } 
 
    }); 
 
    } 
 

 
    next(i); 
 

 
    return defer.promise; 
 
}

To znalazłem dał nam największą elastyczność. Możesz automatycznie przesuwać operacje itp., Aby utworzyć listę instrukcji, a także możesz dołączyć do nich tyle funkcji obsługi .then() w funkcji wywołującej. Możesz także łańcuchować wiele funkcji syncPromises, które będą się działy w kolejności.

0

Nie możesz - nie ma żadnych funkcji w angular, Q (obietnice) lub JavaScript (w tym momencie), które pozwalają to zrobić.

Będziesz, gdy stanie się ES7.

Możesz użyć, jeśli używasz innego frameworka lub transplera (zgodnie z sugestią w artykule połączonym - Traceur transpiler lub Spawn).

Możesz, jeśli rzucisz własną implementacją!

Powiązane problemy