简体   繁体   中英

Let Node wait till all data is loaded to send response

Situation sketch

I'm making a web server with Node.js with and a . I'll represent some series into a list on the home fragment of my Android application. Something like image below:

列表是一系列数组

The red rectangle is one list with multiple series.

Data

In the database, I've create a collection named lists . Here is an example of a document:

{
    "_id" : ObjectId("486464a459f14e486012ee4a"),
    "name" : "Flemish",
    "series" : [ 61519, 64095, 11431, 16148, 63315, 68667, 8318, 61548, 62025, 36960 ]
}

The property series got a different length in each document.

The numbers in that array, are representing ID's from series. They are comming from The Movie Database (TMDb) .

What I will

Now I'll merge my list from my database with the data from TMDb. For this, I've made this code:

const dbService = require("./../data/databaseService.js"),
      apiService = require("./../data/apiService.js"),
      express = require("express"),
      router = express.Router();

router.get("/list", (req, res, next) => {

    dbService.getLists((err, data) => {
        if (err) {
            next(err);
        }
        else {

            let seriesData = [];

            for (var listIndex = data.length - 1; listIndex--;) {

                let temp = { 
                    name: data[listIndex].name, 
                    series: [] 
                };

                for (var seriesIndex = data[listIndex].series.length - 1; seriesIndex--;) {

                    let id = data[listIndex].series[seriesIndex];

                    apiService.request(`tv/${id}?append_to_response=images,similar`, (err, data) => {
                        if (err) {
                            next(err);
                        }
                        else {
                            temp.series.push(data);
                        }
                    });
                }

                seriesData.push(temp);
            }

            res.send(seriesData);
        }
    });
});

module.exports = router;

The problem

The problem I've got is that the line res.send(seriesData); is called before the data is pushed to the array seriesData . This happen on this line temp.series.push(data); . Below you find the code that will be send:

[
    {
        "name": "British",
        "series": []
    },
    {
        "name": "American",
        "series": []
    },
    {
        "name": "Reality",
        "series": []
    },
    {
        "name": "Flemish",
        "series": []
    }
]

I know everything happens async with Node.js and I haven't been shocked by the result but now Node must wait.

The question

Now my question is, can the response wait till all data is loaded from TMDb? If yes how, else why?

Note also that the items in the lists must be equal. Example: the series "Game of thrones" stands in the American list and can not be pushed to another list. Same thing with the series "Als de dijken breken" . This series stand into the Flemish list and cannot be pushed into British.

Reference

I've made an image where you can check the code again and got the results of each request and response as JSON code.

参考 (Click on image to see real size)

After searching and testing I've finally found a solution.

The library

For library I use Async.js . You can install that by using code below in your command line:

npm install --save async

And added the library by using this

const async = require("async");

I use the async.each() methode.

This is the simpler solution to the problem. The function takes an array of items, then iterates over them calling a wrapper function which accepts the item as an argument. When all the calls are complete, you specify a final function to be called.

 // 1st para in async.each() is the array of items async.each(items, // 2nd param is the function that each item is passed to function(item, callback){ // Call an asynchronous function, often a save() to DB item.someAsyncCall(function (){ // Async call is done, alert via callback callback(); }); }, // 3rd param is the function to call when everything's done function(err){ // All tasks are done now doSomethingOnceAllAreDone(); } ); 

Source: justinklemm.com - Node.js Async tutorial

Hint: make use of named functions.

The code

Below you can find the code I use:

const dbService = require("./../data/databaseService.js"),
      apiService = require("./../data/apiService.js"),
      express = require("express"),
      async = require("async"),
      router = express.Router();

router.get("/list", (req, res, next) => {

    dbService.getLists((err, data) => {
        if (err) {
            next(err);
        }
        else {

            let seriesData = [],
                apiRequests = [];

            for (let listIndex = data.length; listIndex--;) {

                let theName = data[listIndex].name;

                seriesData.push({
                    name: theName,
                    series: []
                });

                for (let seriesIndex = data[listIndex].series.length; seriesIndex--;) {

                    let id = data[listIndex].series[seriesIndex];

                    apiRequests.push({
                        destinationListName: theName,
                        tmdbId: id
                    });
                }
            }

            let apiCall = (apiReq, cb) => {
                apiService.request(`tv/${apiReq.tmdbId}?append_to_response=images,similar`, (err, data) => {
                    if (err) {
                        next(err);
                    }
                    else {
                        for (let listIndex = seriesData.length; listIndex--;) {
                            let name = seriesData[listIndex].name;

                            if (name == apiReq.destinationListName) {
                                seriesData[listIndex].series.push(data);
                                cb();
                            }
                        }
                    }
                });
            },
            afterApiCall = (err) => {
                if (err) {
                    next(err);
                }
                else {
                    res.send(seriesData);
                }
            };

            async.each(apiRequests, apiCall, afterApiCall);
        }
    });
});

module.exports = router;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM