@ndbn

Как реализовывать структура скрипта с асинхронными запросами?

Вопрос не новый, но я не пойму как организовывать скрипт для нормальной работы. Проблема, конечно-же в асинхронных запросах.
Задача проста - спарсить данные с сайта(REST API);
Я представляю так(псевдо-псевдо-код):
//  функция_получить_число_записей  - уже должна выполнить запрос, чтобы получить число записей. 
// Запрос асинхронный.
var cnt = функция_получить_число_записей();

for(каждая запись)
{
  //функция_получить_запись - запрос данных с сайта. Запрос асинхронный.
   var данные = функция_получить_данные(запись);
    функция_обработать_данные(данные);
}


var request = require('request-json');
var Q = require('q');

var client = request.createClient(SITE_URL);

// Обертка для асинхнонной функции
// (https://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/)
function client_post(path, data, callback) {
    var deferred = Q.defer();

    client.post(path, data, function (err, res, body) {
        if (err)
            deferred.reject(err); // rejects the promise with `er` as the reason
        else
            deferred.resolve(body); // fulfills the promise with `data` as the value
    });

    return deferred.promise.nodeify(callback); // the promise is returned
}


//Отправляет запрос на сайт
function requestData(data)
{    client_post(SITE_PATH, data, function (err, res) {
        return res;
    });
}

//Создает JSON объект с параметрами запроса
function createDataStruct(pageNumber){ ... }

var data = createDataStruct(61, 1, 1);
var res = requestData(data);


Я подсмотрел на одном сайте как создать promise - версию асинхронной функции (client_post), но это же абсолютно ничего не дает, вместо использования callback' ка самой функции, мы используем коллбэк который передается как параметр. В общем не понимаю, скажите что посмотреть, почитать, какой подход использовать.
Спасибо!
  • Вопрос задан
  • 220 просмотров
Решения вопроса 2
kirill89
@kirill89
Можно воспользоваться магией ES6 и bluebirdjs:

const request = require('request-json');
const Promise = require('bluebird');

let client = request.createClient(SITE_URL);

client.post = Promise.promisify(client.post);

Promise.coroutine(function* () {
    let data1 = createDataStruct(61, 1, 1);
    let result1 = yield client.post(SITE_PATH, data);

    let data2 = createDataStruct(61, 1, 1);
    let result2 = yield client.post(SITE_PATH, data);

    console.log(result1, result2);
})();


Не проверял будет ли работать, но очень советую посмотреть в направлении корутин, генераторов и особенно в сторону bluebird.
Ответ написан
Комментировать
@vshvydky
срочно надо познакомиться с следующими модулями, они вам дадут билет в жизнь
1. request-promise оборачивает запросы в промисы
2. co / vo любой по настроению, отлично работает с генераторами, может делать асинхронный код синхронным, если нужно очередность действий получать.

Если будете промис использовать в генераторе, помните, что то, что обычно попадает в catch будет генерировать ошибку , так что ловить все надо через try catch
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@de1m
Недавно что-то такое-же надо было. К примеру - у меня есть массив с серверами и на каждом сервере у меня есть несколько папок, которые надо проверить.
self.startVerifyBackup(listOfServer).then(function (outArr) {
        return callback(null, outArr);
      }).catch(function (err) {
        return callback(err, null);
      });

Здесть я передаю массив с серверам.
Вот сама функция
verifyBackup.prototype.startVerifyBackup = function (folderList) {
  var self = this;

  return Promise.all(folderList.map(function (folder) {
    return self.checkFolder(folder);
  }));
};

И функция которая проверяет все папки на каждом сервере
verifyBackup.prototype.checkFolder = function (folder) {
  var self = this;
  return new Promise(function (resolve, reject) {
    if (self.config.backupserver[self.bserver].passphrase !== undefined && self.config.backupserver[self.bserver].passphrase !== null) {
      var passphrase = "PASSPHRASE=" + self.config.backupserver[self.bserver].passphrase;
    } else {
      var passphrase = "";
    }
    if (self.config.backupserver[self.bserver].tmpdir !== undefined && self.config.backupserver[self.bserver].tmpdir !== null) {
      var btemp = self.config.backupserver[self.bserver].tmpdir;
    } else {
      var btemp = "";
    }

    var command = "sudo " + passphrase + " duplicity verify file://" + folder + " " + btemp;
    self.runSSH(command, self.sshkeypath, self.eserver, self.bserver, function (err, data) {
      if (err) {
        return reject(err);
      } else {

        var output = data.toString().split("\n");
        output.splice(0, 2);
        var objReturn = {
          name: folder,
          output: output
        };
        return resolve(objReturn);
      }
    });
  });
};


Примерно так это должно выглядить, хотя я думаю, что у меня ошибки немного не верно обрабатываются.
Ещё вместо promis'ов можно использовать библиотеку async. Но адепты promis'ов против, хотя там больше возможностей.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы