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

Jak wyciągnąć resultat wewnętrznej funkcji callback?
andrzej.kmicic
  • Rejestracja:ponad 12 lat
  • Ostatnio:około 2 miesiące
  • Postów:100
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" :

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

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

moduł ./SMA/index

Kopiuj

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:

Kopiuj

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

Kopiuj
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 :

Kopiuj

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

edytowany 3x, ostatnio: andrzej.kmicic
Maciej Cąderek
Maciej Cąderek
  • Rejestracja:około 10 lat
  • Ostatnio:prawie 4 lata
  • Lokalizacja:Warszawa
  • Postów:1264
1

Lepiej zamiast:

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

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

uprościć to do:

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

  talib.execute(someData, callback);
};
Kopiuj
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:

Kopiuj
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:

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

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

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

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

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

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

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

main()
edytowany 6x, ostatnio: Maciej Cąderek
andrzej.kmicic
Super, dzięki za chcenie :) ... Mam trochę do sprawdzania :). pozdrawiam AK
andrzej.kmicic
Sprawdzam i jestem pod wrażeniem i jeszcze raz dzięki. Ho ho, ależ Ty mądrala !!!. Powinieneś z tą wiedzą książkę popełnić. I masz do tego dar przekazywania istotnych informacji. Zawsze podchodziłem do asynchro jak do jeża a okazuje się to całkiem git.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.