Jak wyciągnąć resultat wewnętrznej funkcji callback?

0

Witam

Uczę się na starość, node.js i mam taki problem :
mam plik główny app.js a w nim wywołuje moduł indic_sma zapisany w katalogu "./lib/SMA/index" :

var indic_sma = require("./lib/SMA/index");

console.log("SMA Function Results:");
console.log(indic_sma.sma());

moduł ./SMA/index


var talib = require('talib');

exports.sma = function() {
    var marketData = { close: [12,5,6,4,7,1,0,3,4,5,6,7,0,1,12,1,3,0,1,5,8,2,4,5,7,3,1,4]};
talib.execute({
    name: "SMA",
    startIdx: 0,
    endIdx: marketData.close.length - 1,
    inReal: marketData.close,
    optInTimePeriod: 5,
}, function (err, result) {
    console.log("Result:");
    console.log(err);
    console.log(result);
    });
}

jeżeli w wewnętrznym ciele funkcji callback [function (err, result)] modułu drukuję rezultat do konsoli to wszystko jest ok. Natomiast nie wiem jak zwrócić objekt (err,result) do modułu głównego app.js
Czytałem cos o zrobieniu czegos w rodzaju proxy, cytuję fragment z książki "Node.js Design Patterns":

Batching requests in the total sales web server

Let's now add a batching layer on top of our totalSales API. The pattern we are going to use is very simple: if there is already another identical request pending then the API is invoked, we will add the callback to a queue. When the asynchronous operation completes, all the callbacks in its queue are invoked at once.
Now, let's see how this pattern translates in code. Let's create a new module named totalSalesBatch.js. Here, we're going to implement a batching layer on top of the original totalSales API:


var totalSales = require('./totalSales');
var queues = {};

module.exports = function totalSalesBatch(item, callback) {
  if(queues[item]) {             //[1]
    console.log('Batching operation');
    return queues[item].push(callback);
  }
  queues[item] = [callback];          //[2]
  totalSales(item, function(err, res) {
    var queue = queues[item];           //[3]
    queues[item] = null;
    queue.forEach(function(cb) {
      cb(err, res);
    });
  });
}

The core of the module is the totalSales function, which is also the only exported API, this is how it works:

  1. We create a stream from the salesDb sublevel that contains the sales transactions. The stream pulls all the entries from the database.
  2. The data event receives each sale transaction as it is returned from the database stream. We add the amount value of the current entry to the total sum value, but only if the item type is equal to the one provided in the input (or if no input is provided at all, allowing us to calculate the sum of all the transactions, regardless of the item type).
  3. At last, when the end event is received, we invoke the
    callback() method by providing the final sum as result.

ale czegoś jeszcze w tym programowaniu asynchronicznym nie rozumiem i dokąd tego nie załapię to nie mogę sobie poradzić w tym konkretnym przypadku. Myślę że ten mój przykład jest prostszy niż ten książkowy ale tez nie mogę dać rady. Pomroczność jasna czy coś podobnego?. Jakby ktoś mógł pomóc i pogonić tego niemca Altzheimera co za mną chodzi, to będę kontent :)

pozdrawiam
AK.

dodano chwilę później : Wymęczyłem wreszcie, przepraszam za kłopot ale może się przyda komuś albo ktoś podsunie inne elegantsze (lepsze) rozwiązanie. Należy jako parametru wywołania funkcji eksportowanej uzyć parametru jako callback (funkcja) i wywoływać go z modułu głównego własnie z tym parametrem :
var talib = require('talib');

exports.sma = function (callback) {
    var marketData = { close: [12, 5, 6, 4, 7, 1, 0, 3, 4, 5, 6, 7, 0, 1, 12, 1, 3, 0, 1, 5, 8, 2, 4, 5, 7, 3, 1, 4] };
    talib.execute({
        name: "SMA",
        startIdx: 0,
        endIdx: marketData.close.length - 1,
        inReal: marketData.close,
        optInTimePeriod: 5,
    }, function (err, result) {
        callback({ err, result });
    });
}

i wołamy z modułu głównego :


var x= indic_sma.sma(function(obj){
        console.log(obj.err); 
        console.log(obj.result); 
});

dziękuję i sorry za kłopot. Chyba zaczynam łapać o co chodzi w tym wołaniem modułów w node ale myślę że to tylko początek. Niebywałe wrzucam mu funkcję a on bez wyraźnego polecenia return, zwraca przez parametr jej wartość. Kto to tak powikłał :)...
To tak jakby podsunąć modułowi środek transportu a ten ładuje tam bagaże i z głowy problem...:)
pozdr
AK

1

Lepiej zamiast:

exports.sma = function(callback) {
  var someData = {};

  talib.execute(someData, function(err, result) {
    callback({ err, result });
  });
};
var x = indic_sma.sma(function(obj) {
  if (obj.err) {
    /* error handling */
  } else {
    /* result handling */
  }
});

uprościć to do:

exports.sma = function(callback) {
  var someData = {};

  talib.execute(someData, callback);
};
var x = indic_sma.sma(function(err, result) {
  if (err) {
    /* error handling */
  } else {
    /* result handling */
  }
});

Możesz też skorzystać z dobrodziejstw ES6+ (z czym nie ma problemu, bo aktualne wersje Node'a to obsługują) i działać na promisach, na początek trzeba opakować funkcję z callbackiem w wersję zwacającą promisa:

const executeAsync = (data) => new Promise((resolve, reject) => {
  talib.execute(data, (err, result) => {
    if (error) {
      reject(error)
    } else {
      resolve(result)
    }
  })
})

lub lepiej za pomoca biblioteki standardowej Node'a:

const util = require('util')
const executeAsync = util.promisify(talib.execute)

Teraz możesz tego uzyć w wygodniejszy sposób:

exports.sma = () => {
  const someData = {}

  return executeAsync(someData)
}
indic_sma.sma()
  .then(result => {/* result handling */})
  .catch(error => {/* error handling */})

lub za pomoca async-await (kod wygląda prawie jak synchroniczny):

exports.sma = () => {
  const someData = {}

  return executeAsync(someData)
}
const main = async () => {
  try {
    const result = await indic_sma.sma()
    /* result handling */
  } catch (err) {
    /* error handling */
  }
}

main()

1 użytkowników online, w tym zalogowanych: 0, gości: 1