Як записати асинхронні функції для Node.js


114

Я намагався дослідити, як саме слід писати асинхронні функції. Після багато орання через багато документації мені все ще незрозуміло.

Як записати асинхронні функції для Node? Як я можу правильно реалізувати поводження з помилками?

Іншим способом задати моє запитання було б таке: Як я повинен інтерпретувати наступну функцію?

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

Крім того, мені було цікавим це запитання щодо SO ("Як створити неблокуючу асинхронну функцію в node.js?"). Я не відчуваю, як на нього ще відповіли.


14
Ось чому я прошу. Мені не видно, чим ці функції відрізняються.
Kriem

Я рекомендую вам подивитися на setTimeoutі setIntervalв вашому улюбленому браузері і грати з ними. Або зворотні виклики Ajax (можливо, найбільш близькі до досвіду роботи вузла), або слухачі подій для речей, з якими ви знайомі, як-от події клацання та завантаження. Асинхронна модель існує вже в браузері, і вони абсолютно однакові у вузлі.
davin

@davin - Гадаю, я не повністю розумію асинхронну модель тоді.
Kriem

@Kriem, я вчора відповів на щось, що може допомогти: stackoverflow.com/questions/6883648/… Це не відповідь на ваше запитання, але це на тему. Спробуйте і прочитайте там питання і відповідь і пограйте з кодом, щоб спробувати зрозуміти, що відбувається.
davin

2
@Raynos Що таке визначення "асинхронна функція"?
Андерсон Грін

Відповіді:


85

Здається, ви плутаєте асинхронний IO з асинхронними функціями. node.js використовує асинхронний незаблокувальний IO, тому що не блокуючи IO краще. Найкращий спосіб зрозуміти це - подивитись деякі відео від Райана Даля.

Як записати асинхронні функції для Node?

Просто запишіть звичайні функції, різниця лише в тому, що вони не виконуються відразу, а передаються навколо як зворотні дзвінки.

Як правильно реалізувати поводження з помилками

Як правило, API дає вам зворотний виклик із помилкою як перший аргумент. Наприклад

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

Це загальна закономірність.

Ще одна поширена модель on('error'). Наприклад

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

Редагувати:

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

Вищеописана функція при виклику як

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

Друкуватиметься 42на консолі асинхронно. Зокрема, process.nextTickпожежі після того, як поточний стовпчик виклику подій порожній. Цей стек викликів порожній після async_functionі console.log(43)закінчився. Отже, ми друкуємо 43, а далі - 42.

Напевно, вам слід прочитати цикл подій.


Я бачив вигляд Даля, але, здається, я не розуміюсь цього питання. :(
Kriem

1
@Kriem дивіться оновлену відповідь та читайте про цикл подій
Raynos

1
Дякую за форт. Зараз я більше усвідомлюю те, чого мені бракує знань. :) Ваш останній приклад до речі допоміг.
Крим

Я думаю, ви твердження про асинхронний IO "краще" занадто загальне. У цьому сенсі так, але в цілому це може бути не так.
Джейк Б

У своєму першому прикладі коду ви перевіряєте аргумент помилки, але після цього не поверталися. У разі помилки код продовжить і, можливо, спричинить серйозні проблеми у вашій програмі.
Габріель МакАдамс

9

Просто пройти повз зворотних дзвінків недостатньо. Наприклад, вам потрібно використовувати settimer, щоб зробити функцію асинхронізації.

Приклади: Не асинхронні функції:

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");

Якщо ви запустите вище приклад, це має бути добре, доведеться чекати, поки ці функції закінчать працювати.

Псевдо багатопотокові (асинхронні) функції:

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");

Цей буде по-справжньому асинхронним. Це має бути добре, буде написано до завершення асинхронізації.



3

Якщо ви знаєте, що функція повертає обіцянку, я пропоную використовувати нові функції async / wait в JavaScript. Завдяки цьому синтаксис виглядає синхронним, але працює асинхронно. Коли ви додаєте asyncключове слово до функції, воно дозволяє awaitобіцянки в цій області:

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

  console.log(r); // true
}

якщо функція не повертає обіцянку, я рекомендую перетворити її в нову визначену вами обіцянку, а потім вирішіть потрібні дані:

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: ... }
}

Підсумок: використовуйте силу Обіцянь.


Що тут пам’ятати, тіло обіцянки все ще виконується синхронно.
тінь0359

2

Спробуйте це, він працює як для вузла, так і для браузера.

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);
});

18
4 голоси та навіть не один конструктивний коментар ..: \
Омер

6
@Omer Таке життя на SO.
Piece Digital

6
@NorbertoBezi Можливо, код вам зрозумілий, але не тому, хто опублікував відповідь. Ось чому це завжди є хорошою практикою пояснювати їх після відхилення.
Омер

0

Я маю справу надто багато годин для такого завдання в node.js. Я в основному фронтовий хлопець.

Я вважаю це досить важливим, оскільки всі методи вузлів асинхронно справляються із зворотним викликом, і перетворити його в Promise краще з цим.

Я просто хочу показати можливий результат, більш худий і читабельний. Використовуючи ECMA-6 з асинхронією, ви можете записати це так.

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

(undefined || null)для Repl (читання подій циклу друку) сценаріїв, використовуючи невизначені також роботу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.