Jak pisać funkcje asynchroniczne dla Node.js.

114

Próbowałem zbadać, jak dokładnie należy pisać funkcje asynchroniczne. Po wielu przeglądach wielu dokumentów nadal nie jest to dla mnie jasne.

Jak pisać funkcje asynchroniczne dla Node? Jak poprawnie zaimplementować obsługę zdarzeń błędów?

Innym sposobem zadania pytania byłoby: Jak powinienem zinterpretować następującą funkcję?

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

Wydaje mi się również, że to pytanie dotyczące SO („Jak utworzyć nieblokującą funkcję asynchroniczną w node.js?”) Jest interesujące. Nie wydaje mi się, że została jeszcze udzielona odpowiedź.

Kriem
źródło
14
Dlatego pytam. Nie jest dla mnie jasne, czym różnią się te funkcje.
Kriem
Polecam zajrzeć setTimeouti setIntervalw ulubionej przeglądarce i bawić się z nimi, jak również. Lub wywołania zwrotne Ajax (prawdopodobnie najbliższe doświadczeniu węzła) lub nasłuchiwanie zdarzeń dla rzeczy, które znasz, takich jak zdarzenia kliknięcia i ładowania. Model asynchroniczny istnieje już w przeglądarce i jest dokładnie taki sam w węźle.
davin
@davin - Chyba nie rozumiem wtedy w pełni modelu asynchronicznego.
Kriem
@Kriem, wczoraj odpowiedziałem na coś, co może pomóc: stackoverflow.com/questions/6883648/… To nie jest odpowiedź na twoje pytanie, ale jest na temat. Spróbuj przeczytać pytanie i odpowiedzieć na nie i pobawić się kodem, aby spróbować zrozumieć, co się dzieje.
davin
2
@Raynos Jaka jest definicja „funkcji asynchronicznej”?
Anderson Green

Odpowiedzi:

85

Wydaje się, że mylisz asynchroniczne operacje we / wy z funkcjami asynchronicznymi. node.js używa asynchronicznego, nieblokującego IO, ponieważ nieblokujące IO jest lepsze. Najlepszym sposobem, aby to zrozumieć, jest obejrzenie kilku filmów autorstwa Ryana Dahla.

Jak pisać funkcje asynchroniczne dla Node?

Po prostu napisz zwykłe funkcje, jedyną różnicą jest to, że nie są one wykonywane natychmiast, ale przekazywane jako wywołania zwrotne.

Jak poprawnie zaimplementować obsługę zdarzeń błędów

Generalnie API daje wywołanie zwrotne z błędem jako pierwszym argumentem. Na przykład

database.query('something', function(err, result) {
  if (err) handle(err);
  doSomething(result);
});

To powszechny wzór.

Innym powszechnym wzorcem jest on('error'). Na przykład

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});

Edytować:

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

Powyższa funkcja wywoływana jako

async_function(42, function(val) {
  console.log(val)
});
console.log(43);

Drukuje 42na konsoli asynchronicznie. W szczególności process.nextTickuruchamia się, gdy aktualny stos wywołań pętli zdarzeń jest pusty. Ten stos wywołań jest pusty po async_functioni console.log(43)został uruchomiony. Więc drukujemy 43, a następnie 42.

Prawdopodobnie powinieneś poczytać o pętli zdarzeń.

Raynos
źródło
Widziałem filmy Dahla, ale nie mam pojęcia, czego się boję. :(
Kriem
1
@Kriem zobacz zaktualizowaną odpowiedź i przeczytaj o pętli zdarzeń
Raynos
1
Dzięki za wgląd. Jestem teraz bardziej świadomy tego, czego mi brakuje w wiedzy. :) Twój ostatni przykład pomógł przy okazji.
Kriem
Myślę, że stwierdzenie, że asynchroniczne we / wy jest „lepsze” jest zbyt ogólne. W tym sensie tak, ale ogólnie może tak nie być.
Jake B,
W pierwszym przykładzie kodu sprawdzasz argument err, ale później nie wrócił. W przypadku błędu kod będzie kontynuowany i może spowodować poważne problemy w aplikacji.
Gabriel McAdams,
9

Samo przekazywanie informacji zwrotnych nie wystarczy. Aby funkcja była asynchroniczna, musisz na przykład użyć setimer.

Przykłady: funkcje inne niż asynchroniczne:

function a() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };
  b();
};

function b() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };    
  c();
};

function c() {
  for(i=0; i<10000000; i++) {
  };
  console.log("async finished!");
};

a();
console.log("This should be good");

Jeśli uruchomisz powyższy przykład, to powinno być dobre, będziesz musiał poczekać, aż te funkcje zakończą się.

Funkcje pseudo wielowątkowe (asynchroniczne):

function a() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };
    b();
  }, 0);
};

function b() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };  
    c();
  }, 0);
};

function c() {
  setTimeout ( function() {
    for(i=0; i<10000000; i++) {
    };
    console.log("async finished!");
  }, 0);
};

a();
console.log("This should be good");

Ten będzie naprawdę asynchroniczny. Powinno być dobrze napisane przed zakończeniem asynchronizacji.

spokój
źródło
3

Jeśli WIESZ, że funkcja zwraca obietnicę, sugeruję użycie nowych funkcji async / await w JavaScript. Sprawia, że ​​składnia wygląda na synchroniczną, ale działa asynchronicznie. Dodanie asyncsłowa kluczowego do funkcji umożliwia awaitskładanie obietnic w tym zakresie:

async function ace() {
  var r = await new Promise((resolve, reject) => {
    resolve(true)
  });

  console.log(r); // true
}

jeśli funkcja nie zwraca obietnicy, zalecam zawinięcie jej w nową obietnicę, którą zdefiniujesz, a następnie rozwiąż dane, które chcesz:

function ajax_call(url, method) {
  return new Promise((resolve, reject) => {
    fetch(url, { method })
    .then(resp => resp.json())
    .then(json => { resolve(json); })
  });
}

async function your_function() {
  var json = await ajax_call('www.api-example.com/some_data', 'GET');
  console.log(json); // { status: 200, data: ... }
}

Konkluzja: wykorzystaj siłę obietnic.

ryanwaite28
źródło
Należy tu pamiętać, że obietnica jest nadal wykonywana synchronicznie.
shadow0359
2

Spróbuj tego, działa to zarówno dla węzła, jak i przeglądarki.

isNode = (typeof exports !== 'undefined') &&
(typeof module !== 'undefined') &&
(typeof module.exports !== 'undefined') &&
(typeof navigator === 'undefined' || typeof navigator.appName === 'undefined') ? true : false,
asyncIt = (isNode ? function (func) {
  process.nextTick(function () {
    func();
  });
} : function (func) {
  setTimeout(func, 5);
});
Pradeep
źródło
18
4 głosy przeciw i ani jeden konstruktywny komentarz ..: \
Omer,
6
@Omer Takie jest życie na SO.
Kawałek cyfrowy
6
@NorbertoBezi Być może kod jest dla Ciebie zrozumiały, ale nie dla tego, który opublikował odpowiedź. Dlatego zawsze dobrą praktyką jest wyjaśnienie podczas głosowania w dół.
Omer
0

Mam za dużo godzin na takie zadanie w node.js. Jestem głównie front-endem.

Uważam to za dość ważne, ponieważ wszystkie metody węzłów asynchroniczne obsługują wywołania zwrotne i przekształcają je w Promise, lepiej sobie z tym poradzić.

Chcę tylko pokazać możliwy wynik, bardziej przejrzysty i czytelny. Używając ECMA-6 z async, możesz napisać to w ten sposób.

 async function getNameFiles (dirname) {
  return new Promise((resolve, reject) => {
    fs.readdir(dirname, (err, filenames) => {
      err !== (undefined || null) ? reject(err) : resolve(filenames)
    })
  })
}

(undefined || null)jest rEPL (czytaj pętlowych druku Event) scenariuszy, używając undefined, także pracę.

Yoarthur
źródło