Как писать асинхронные функции для Node.js

114

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

Как мне написать асинхронные функции для Node? Как правильно реализовать обработку событий ошибок?

Другой способ задать свой вопрос: как мне интерпретировать следующую функцию?

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

Кроме того, мне показался интересным этот вопрос о SO («Как мне создать неблокирующую асинхронную функцию в node.js?»). Мне кажется, что на него еще нет ответа.

Kriem
источник
14
Вот почему я спрашиваю. Мне не очевидно, чем отличаются эти функции.
Kriem 01
Я рекомендую вам посмотреть setTimeoutи setIntervalв своем любимом браузере и поиграть с ними. Или обратные вызовы ajax (возможно, наиболее близкие к опыту работы с узлами), или прослушиватели событий для вещей, с которыми вы знакомы, например, событий щелчка и загрузки. Асинхронная модель уже существует в браузере, и они точно такие же в node.
Дэвин 01
@davin - Думаю, я тогда не совсем понимаю асинхронную модель.
Kriem 01
@Kriem, вчера я ответил на кое-что, что могло бы помочь: stackoverflow.com/questions/6883648/… Это не ответ на ваш вопрос, но он по теме. Попробуйте прочитать вопрос и ответ там и поиграйте с кодом, чтобы попытаться понять, что происходит.
Дэвин 01
2
@Raynos Что означает «асинхронная функция»?
Андерсон Грин

Ответы:

85

Кажется, вы путаете асинхронный ввод-вывод с асинхронными функциями. node.js использует асинхронный неблокирующий ввод-вывод, потому что неблокирующий ввод-вывод лучше. Лучший способ понять это - пойти посмотреть несколько видео Райана Даля.

Как мне написать асинхронные функции для 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 срабатывает после того, как текущий стек вызовов eventloop пуст. Этот стек вызовов пуст после выполнения async_functionи console.log(43)выполнения. Итак, мы печатаем 43, а затем 42.

Вам, вероятно, следует почитать цикл событий.

Raynos
источник
Я видел видеоролики Даля, но, боюсь, я не понимаю этого вопроса. :(
Kriem 01
1
@Kriem см. Обновленный ответ и прочтите о цикле событий
Raynos
1
Спасибо за понимание. Теперь я лучше понимаю, чего мне не хватает. :) Кстати, помог ваш последний пример.
Kriem 01
Я думаю, что ваше утверждение об асинхронном вводе-выводе «лучше» является слишком общим. В этом смысле да, но в целом это может быть не так.
Jake B
В первом примере кода вы проверяете аргумент err, но потом не возвращались. В случае ошибки код будет продолжен и потенциально вызовет серьезные проблемы в вашем приложении.
Габриэль Макадамс
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");

Этот будет действительно асинхронным. Это должно быть хорошо, будет написано до завершения async.

calmbird
источник
3

Если вы ЗНАЕТЕ, что функция возвращает обещание, я предлагаю использовать новые функции async / await в 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: ... }
}

Итог: используйте мощь обещаний.

ryanwaite28
источник
Здесь следует помнить, что тело обещания по-прежнему выполняется синхронно.
shadow0359
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 Такова жизнь на ТАК.
Piece Digital
6
@NorbertoBezi Может быть, этот код говорит сам за себя, но не для того, кто опубликовал ответ. Вот почему всегда полезно объяснять голосование против.
Омер
0

Я потратил слишком много часов на такую ​​задачу для node.js. Я в основном фронтенд-парень.

Я считаю это очень важным, потому что все асинхронные методы узла имеют дело с обратным вызовом, и преобразование его в Promise лучше справляется с этим.

Я просто хочу показать возможный результат, более компактный и читаемый. Используя ECMA-6 с async, вы можете написать это так.

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

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

Yoarthur
источник