2012-03-27 10 views
17

Mam funkcję w mojej aplikacji nodejs o nazwie get_source_at. Wymaga uri jako argumentu, a jego celem jest zwrócenie kodu źródłowego z tego uri. Mój problem polega na tym, że nie wiem, jak wywołać funkcję synchronicznie wywołującą żądanie, zamiast podawać tę funkcję zwrotną. I chce kontrolować przepływ, aby zatrzymać się na kilka sekund, aby załadować uri. Jak mogę to osiągnąć?Jak mogę ustawić to wywołanie w synchronizacji nodejs?

function get_source_at(uri){ 
    var source; 
    request({ uri:uri}, function (error, response, body) { 
     console.log(body); 
    }); 
    return source; 
} 

Ponadto przeczytałem o "zdarzeniach" oraz o tym, jak węzeł jest "zdarzenie" i powinienem uszanować to na piśmie mojego kodu. Cieszę się, że mogę to zrobić, ale muszę mieć sposób, aby upewnić się, że mam kod źródłowy z uri przed kontynuowaniem przepływu kontrolnego mojej aplikacji - więc jeśli to nie jest przez synchronizację funkcji, jak to zrobić ?

+0

Podczas gdy odpowiedź Lai jest poprawna pod względem technicznym (dlatego zostałem oznaczony jako odpowiedź), moje ewentualne rozwiązanie wymagało więcej pracy. Gdy masz nieokreśloną liczbę połączeń do żądania, możesz użyć wzorca w przykładowym kodzie znajdującym się pod adresem https://gist.github.com/2234466 – Trindaz

Odpowiedz

16

Powinieneś unikać synchronicznych żądań. Jeśli potrzebujesz czegoś w rodzaju synchronicznego sterowania przepływem, możesz użyć async.

async.waterfall([ 
    function(callback){ 
     data = get_source_at(uri); 
     callback(null, data); 
    }, 
    function(data,callback){ 
     process(data, callback); 
    }, 
], function (err,result) { 
    console.log(result) 
}); 

The process obiecał być uruchamiany poget_source_at zwrotów.

+16

"Powinieneś unikać synchronicznych żądań" nie jest dokładnym zbiorczym stwierdzeniem. Należy unikać synchronicznych żądań wykonywanych w głównym wątku aplikacji lub w innych sytuacjach, które spowodują zablokowanie interfejsu użytkownika. Ale w przypadku powiedzieć, bezgłowy serwis internetowy, którego jedynym celem jest delegowanie do innej usługi sieciowej i raport z powrotem z wynikiem? Tam synchroniczne żądanie jest idealnie w porządku, a w rzeczywistości lepsze/czystsze od asynchronicznego. Ważne jest, aby zrozumieć swój przypadek użycia i wybrać rozwiązanie, które najlepiej pasuje. To nie jest przypadek zawsze jednego, a nigdy drugiego. – aroth

+1

Zgadzam się z @aroth - używam skryptów nodejs do niestandardowych złożonych zadań wdrożeniowych, w których potrzebuję każdego polecenia do wykonania w ścisłej kolejności. Mogę użyć obietnic, ale nawet 'then()' łańcuchowanie może stać się nieco brzydkie, jeśli masz więcej niż 10 zadań do wykonania w ścisłej kolejności. – JustAMartin

+0

To nie jest odpowiedź na to pytanie. –

0

Po pierwsze, aby ten kod był asynchroniczny, wystarczy umieścić odpowiedni kod wewnątrz wywołania zwrotnego funkcji żądania, co oznacza, że ​​będzie on działał po zakończeniu żądania, ale nie zatrzyma procesora od obsługi innych zadań w aplikacji . Jeśli potrzebujesz go wiele razy, radzę zapoznać się z wersją Synchronous request in Node.js, która opisuje różne metody usprawnienia i omawia różne biblioteki przepływu sterowania.

1

muszę mieć sposób, aby upewnić się, mam kodu źródłowego z URI przed kontynuowaniem przepływ sterowania mojego wniosku - więc jeśli to nie uzależniając synchroniczne funkcji, jak można to zrobić?

Biorąc pod uwagę ten punkt wejścia do aplikacji:

function app(body) { 
    // Doing lots of rad stuff 
} 

kopać go przez pobierania ciała:

request({ uri: uri }, function (error, response, body) { 
    if(err) return console.error(err); 

    // Start application 
    app(body); 
} 

To jest coś trzeba będzie się przyzwyczaić podczas programowania dla node.js (i ogólnie javascript). Istnieją moduły sterowania przepływem, takie jak async (które również polecam), ale musisz się przyzwyczaić do kontynuacji stylu przechodzenia, jak to się nazywa.

14

Można z deasync:

function get_source_at(uri){ 
    var source; 
    request({ uri:uri}, function (error, response, body) { 
     source = body; 
     console.log(body); 
    }); 
    while(source === undefined) { 
     require('deasync').runLoopOnce(); 
    } 
    return source; 
} 
8

Jest lepszy sposób korzystania deasync.

var request = require("request") 
var deasync = require("deasync") 

var getHtml = deasync(function (url, cb) { 
    var userAgent = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36"} 
    request({ 
     url: url, 
     headers: userAgent 
    }, 
    function (err, resp, body) { 
     if (err) { cb(err, null) } 
     cb(null, body) 
    }) 
}) 

var title = /<title>(.*?)<\/title>/ 

var myTitle = getHtml("http://www.yahoo.com").match(title)[1] 
console.log(myTitle) 

Proszę odnieść się do documentation of deasync, można zauważyć, że można użyć
desync(function (n params, cb) {})
aby funkcję gdzie cb powinien wrócić z (err, data). Tak więc funkcje mogą być łatwo ominięte za pomocą funkcji deasync. Ale dla funkcji takich jak request, które nie wracają z cb(err, data).Możesz stworzyć własną funkcję (nazwaną lub anonimową) z niestandardowym formatem oddzwaniania cb(err, data), tak jak to zrobiłem w powyższym kodzie. W ten sposób możesz wymusić niemal dowolną funkcję asynchroniczną, taką jak synchronizacja, czekając, aż wywołanie zwrotne cb(err, data) powróci na inną warstwę javascript (jak mówi dokumentacja). Upewnij się także, że pokryłeś wszystkie sposoby wydostania się z funkcji, którą pakujesz z deasync z wywołania zwrotnego cb(err, data), w przeciwnym razie twój program zostanie zablokowany.

Mam nadzieję, że komuś pomaga!

Aktualizacja:
Nie używaj tego sposób prowadzenia synchronicznych żądań. Użyj Async/Await do zapisywania synchronicznie wyglądającego kodu opartego na obietnicach. Możesz użyć modułu npm request-promise-native, aby uniknąć owijania modułów zapytań własnymi obietnicami.

+1

Uczyń nas oświeceniem Twoją ukrytą wiedzą. Wyraź swój powód downvote w komentarzu. dzięki! –

+1

To jest dobre rozwiązanie. deasync jest znacznie lepszym i eleganckim sposobem wykonywania połączenia synchronizacyjnego. – Shad

1

Posiadanie prostej funkcji blokowania to wielkie ułatwienie dla interaktywnego rozwoju! Funkcja sync (zdefiniowana poniżej) może zsynchronizować każdą obietnicę, radykalnie zmniejszając ilość składni potrzebnej do grania za pomocą interfejsu API i nauczyć się go. Na przykład, oto jak go używać z puppeteer biblioteki dla bezgłowe Chrome:

var browser = sync(puppeteer.connect({ browserWSEndpoint: "ws://some-endpoint"})); 
var pages = sync(browser.pages()) 
pages.length 
1 
var page = pages[0] 
sync(page.goto('https://duckduckgo.com', {waitUntil: 'networkidle2'})) 
sync(page.pdf({path: 'webpage.pdf', format: 'A4'})) 

Najlepsze jest to, każdy z tych linii mogą być modyfikowane aż robi to co chcesz, bez konieczności ponownego biegu lub ponownie wpisz wszystkie poprzednie linie za każdym razem, gdy chcesz je przetestować. Działa to, ponieważ masz bezpośredni dostęp do zmiennych browser i pages od najwyższego poziomu.

Oto jak to działa:

const deasync = require("deasync"); 
const sync = deasync((promise, callback) => promise.then(result) => callback(null, result))); 

Wykorzystuje pakiet deasync wspomniano w innych odpowiedzi. deasync tworzy częściową aplikację do anonimowej funkcji, która dodaje callback jako ostatni argument i blokuje do czasu wywołania callback. callback odbiera warunek błędu jako pierwszy argument (jeśli występuje), a wynik jako drugi (jeśli występuje).

+0

Jest to przydatne w przypadku starszych wersji węzłów, które nie obsługują async/await. –

Powiązane problemy