Korzystanie z asynchronicznego wodospadu w węźle.js

Mam 2 funkcje, które uruchamiam asynchronicznie. Chciałbym je napisać za pomocą modelu wodospadu. Chodzi o to, że nie wiem jak..

Oto Mój kod:

var fs = require('fs');
function updateJson(ticker, value) {
  //var stocksJson = JSON.parse(fs.readFileSync("stocktest.json"));
  fs.readFile('stocktest.json', function(error, file) {
    var stocksJson =  JSON.parse(file);

    if (stocksJson[ticker]!=null) {
      console.log(ticker+" price : " + stocksJson[ticker].price);
      console.log("changing the value...")
      stocksJson[ticker].price =  value;

      console.log("Price after the change has been made -- " + stocksJson[ticker].price);
      console.log("printing the the Json.stringify")
      console.log(JSON.stringify(stocksJson, null, 4));
      fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function(err) {  
        if(!err) {
          console.log("File successfully written");
        }
        if (err) {
          console.error(err);
        }
      }); //end of writeFile
    } else {
      console.log(ticker + " doesn't exist on the json");
    }
  });
} // end of updateJson 

Jakiś pomysł jak Mogę to napisać używając waterfall, żebym mógł to kontrolować? Proszę napisz mi kilka przykładów, ponieważ jestem nowy w node.js

Author: Talha Awan, 2014-09-07

3 answers

Najpierw zidentyfikuj kroki i zapisz je jako funkcje asynchroniczne (biorąc argument wywołania zwrotnego)

  • Przeczytaj plik

    function readFile(readFileCallback) {
        fs.readFile('stocktest.json', function (error, file) {
            if (error) {
                readFileCallback(error);
            } else {
                readFileCallback(null, file);
            }
        });
    }
    
  • Przetwarza plik (usunąłem większość konsoli.Zaloguj się w przykładach)

    function processFile(file, processFileCallback) {
        var stocksJson = JSON.parse(file);
        if (stocksJson[ticker] != null) {
            stocksJson[ticker].price = value;
            fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function (error) {
                if (err) {
                    processFileCallback(error);
                } else {
                    console.log("File successfully written");
                    processFileCallback(null);
                }
            });
        }
        else {
            console.log(ticker + " doesn't exist on the json");
            processFileCallback(null); //callback should always be called once (and only one time)
        }
    }
    

Zauważ, że nie zrobiłem tu żadnej konkretnej obsługi błędów, skorzystam z async.waterfall do scentralizowania obsługi błędów w tym samym miejscu.

Należy również uważać, że jeśli masz (if / else/switch/...) gałęzie w asynchronicznym funkcja, zawsze wywołuje callback jeden (i tylko jeden) raz.

Podłącz wszystko za pomocą async.wodospad

async.waterfall([
    readFile,
    processFile
], function (error) {
    if (error) {
        //handle readFile error or processFile error here
    }
});

Czysty przykład

Poprzedni Kodeks był zbyt gadatliwy, aby wyjaśnienia były jaśniejsze. Oto pełny przykład:

async.waterfall([
    function readFile(readFileCallback) {
        fs.readFile('stocktest.json', readFileCallback);
    },
    function processFile(file, processFileCallback) {
        var stocksJson = JSON.parse(file);
        if (stocksJson[ticker] != null) {
            stocksJson[ticker].price = value;
            fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function (error) {
                if (!err) {
                    console.log("File successfully written");
                }
                processFileCallback(err);
            });
        }
        else {
            console.log(ticker + " doesn't exist on the json");
            processFileCallback(null);
        }
    }
], function (error) {
    if (error) {
        //handle readFile error or processFile error here
    }
});

Zostawiłem nazwy funkcji, ponieważ pomaga czytelność i pomaga debugować za pomocą narzędzi takich jak debugger chrome.

Jeśli użyjesz podkreślenia (na npm ), można również zastąpić pierwszą funkcję z _.partial(fs.readFile, 'stocktest.json')

 52
Author: Volune,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-06 22:24:37

Przede wszystkim upewnij się, że przeczytałeś dokumentację dotyczącą async.waterfall.

Teraz jest kilka kluczowych części o przepływie kontroli wodospadu:

  1. przepływ sterujący jest określony przez tablicę funkcji do wywołania jako pierwszy argument i wywołanie zwrotne "kompletne", gdy przepływ zostanie zakończony jako drugi argument.
  2. tablica funkcji jest wywoływana w serii (W przeciwieństwie do parallel).
  3. Jeśli błąd (Zwykle nazwany err) jest napotkany przy każdej operacji w tablicy przepływu, spowoduje zwarcie i natychmiast wywoła "complete"/"finish"/"done" callback.
  4. argumenty z poprzednio wykonanej funkcji są stosowane do następnej funkcji w przepływie sterowania, w kolejności, a "pośredni" wywołanie zwrotne jest dostarczane jako ostatni argument. Uwaga: pierwsza funkcja ma tylko to "pośrednie" wywołanie zwrotne, a" kompletne " wywołanie zwrotne będzie miało argumenty ostatniej wywołanej funkcji w przepływ sterowania (z uwzględnieniem wszelkich błędów), ale z argumentem err poprzedzonym zamiast "pośredniego" wywołania zwrotnego, który jest dołączany.
  5. wywołania zwrotne dla każdej pojedynczej operacji (wywołuję to cbAsync w moich przykładach) powinny być wywoływane, gdy jesteś gotowy do przejścia: pierwszy parametr będzie błędem, jeśli w ogóle, a drugi (trzeci, czwarty... itd.) parametrem będą wszelkie dane, które chcesz przekazać do następnej operacji.

Pierwszym celem jest zdobycie kodu praca niemal dosłowna wraz z wprowadzeniem async.waterfall. Postanowiłem usunąć wszystkie Twoje console.log wypowiedzi i uprościć obsługę błędów. Oto pierwsza iteracja (nieprzetestowany kod):

var fs = require('fs'),
    async = require('async');

function updateJson(ticker,value) {
    async.waterfall([ // the series operation list of `async.waterfall`
        // waterfall operation 1, invoke cbAsync when done
        function getTicker(cbAsync) {
            fs.readFile('stocktest.json',function(err,file) {
                if ( err ) {
                    // if there was an error, let async know and bail
                    cbAsync(err);
                    return; // bail
                }
                var stocksJson = JSON.parse(file);
                if ( stocksJson[ticker] === null ) {
                    // if we don't have the ticker, let "complete" know and bail
                    cbAsync(new Error('Missing ticker property in JSON.'));
                    return; // bail
                }
                stocksJson[ticker] = value;
                // err = null (no error), jsonString = JSON.stringify(...)
                cbAsync(null,JSON.stringify(stocksJson,null,4));    
            });
        },
        function writeTicker(jsonString,cbAsync) {
            fs.writeFile('stocktest.json',jsonString,function(err) {
                cbAsync(err); // err will be null if the operation was successful
            });
        }
    ],function asyncComplete(err) { // the "complete" callback of `async.waterfall`
        if ( err ) { // there was an error with either `getTicker` or `writeTicker`
            console.warn('Error updating stock ticker JSON.',err);
        } else {
            console.info('Successfully completed operation.');
        }
    });
}

Druga iteracja nieco bardziej dzieli przebieg operacji. Umieszcza go w mniejszych, zorientowanych na jedną operację kawałkach kodu. Nie będę tego komentował, to mówi samo za siebie (ponownie, nieprzetestowane):

var fs = require('fs'),
    async = require('async');

function updateJson(ticker,value,callback) { // introduced a main callback
    var stockTestFile = 'stocktest.json';
    async.waterfall([
        function getTicker(cbAsync) {
            fs.readFile(stockTestFile,function(err,file) {
                cbAsync(err,file);
            });
        },
        function parseAndPrepareStockTicker(file,cbAsync) {
            var stocksJson = JSON.parse(file);
            if ( stocksJson[ticker] === null ) {
                cbAsync(new Error('Missing ticker property in JSON.'));
                return;
            }
            stocksJson[ticker] = value;
            cbAsync(null,JSON.stringify(stocksJson,null,4));
        },
        function writeTicker(jsonString,cbAsync) {
            fs.writeFile('stocktest.json',jsonString,,function(err) {
                cbAsync(err);
            });
        }
    ],function asyncComplete(err) {
        if ( err ) {
            console.warn('Error updating stock ticker JSON.',err);
        }
        callback(err);
    });
}

Ostatnia iteracja krótka-ręce dużo tego z wykorzystaniem niektórych bind niektóre z nich nie są już dostępne, a niektóre z nich nie są dostępne.]}

var fs = require('fs'),
    async = require('async');

function updateJson(ticker,value,callback) {
    var stockTestFile = 'stocktest.json';
    async.waterfall([
        fs.readFile.bind(fs,stockTestFile),
        function parseStockTicker(file,cbAsync) {
            var stocksJson = JSON.parse(file);
            if ( stocksJson[ticker] === null ) {
                cbAsync(new Error('Missing ticker property in JSON.'));
                return;
            }
            cbAsync(null,stocksJson);
        },
        function prepareStockTicker(stocksJson,cbAsync) {
            stocksJson[ticker] = value;
            cbAsync(null,JSON.stringify(stocksJson,null,4));
        },
        fs.writeFile.bind(fs,stockTestFile)
    ],function asyncComplete(err) {
        if ( err ) {
            console.warn('Error updating stock ticker JSON.',err);
        }
        callback(err);
    });
}
 14
Author: zamnuts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-06 22:36:57

Zasadniczo funkcje nodejs (i bardziej ogólnie javascript), które wymagają trochę czasu do wykonania (czy to dla I/O lub przetwarzania cpu) są zazwyczaj asynchroniczne, więc pętla zdarzeń (aby uprościć to jest pętla, która stale sprawdza zadania do wykonania) może wywołać funkcję tuż pod pierwszą, bez blokowania odpowiedzi. Jeśli znasz inne języki, takie jak C lub Java, możesz uznać funkcję asynchroniczną za funkcję, która działa w innym wątku (nie jest w javascript jest to koniecznie prawda, ale programista nie powinien się tym przejmować) i po zakończeniu wykonania wątek ten powiadamia głównego (pętlę eventową), że zadanie zostało wykonane i ma wyniki.

Jak wspomniano, gdy pierwsza funkcja zakończyła swoje zadanie, musi być w stanie powiadomić, że jej zadanie jest zakończone i robi to wywołując funkcję zwrotną, którą mu przekazujesz. aby zrobić przykład:

var callback = function(data,err)
{
   if(!err)
   {
     do something with the received data
   }
   else
     something went wrong
}


asyncFunction1(someparams, callback);

asyncFunction2(someotherparams);

Przepływ wykonania wywoła: asyncFunction1, asyncFunction2 i każdy funkcja poniżej do momentu zakończenia asyncFunction1, wtedy funkcja callback, która jest przekazywana jako ostatni parametr do asyncFunction1 jest wywoływana, aby zrobić coś z danymi, jeśli nie wystąpiły błędy.

Tak więc, aby 2 lub więcej funkcji asynchronicznych wykonywało się jedna po drugiej tylko wtedy, gdy się skończyły, musisz wywołać je wewnątrz ich funkcji zwrotnych:

function asyncTask1(data, function(result1, err)
{
   if(!err)
     asyncTask2(data, function(result2, err2)
     {
           if(!err2)
        //call maybe a third async function
           else
             console.log(err2);
     });
    else
     console.log(err);
});

Result1 jest wartością zwracaną z asyncTask1, a result2 jest wartością zwracaną dla asyncTask2. Możesz w ten sposób zagnieżdżać ile asynchronicznych funkcje, które chcesz.

W Twoim przypadku jeśli chcesz wywołać inną funkcję po updatejson () musisz wywołać ją po tej linii:

console.log("File successfully written");
 2
Author: MastErAldo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-22 20:05:44