简体   繁体   中英

Make multiple calls in nodejs

I have some mock data for below 2 URLS:

1. Get the list of users from 'https://myapp.com/authors'.
2. Get the list of Books from 'https://myapp.com/books'.

Now my task is to sort the Books by name and write the sorted list to the file mysortedbooks.json as JSON

Then I have to create an array of authors with books property that has all the books of that author.

If the author has no books then this array should be empty. Sorting is not needed for this case, and data should be stored in file authorBooks.json as JSON.

Now I have to return a promise that resolves when the above steps are complete. For example, I should return the final saveToFile call in below code.

const fs = require('fs');

function getFromURL(url) {
    switch (url) {
        case 'https://myapp.com/authors':
            return Promise.resolve([
                { name: "Chinua Achebe", id: "1" },
                { name: "Hans Christian Andersen", id: "2" },
                { name: "Dante Alighieri", id: "3" },
            ]);
        case 'https://myapp.com/books':
            return Promise.resolve([
                { name: "Things Fall Apart", authorId: "1" },
                { name: "The Epic Of Gilgamesh", authorId: "1" },
                { name: "Fairy tales", authorId: "2" },
                { name: "The Divine Comedy", authorId: "2" },
                { name: "One Thousand and One Nights", authorId: "1" },
                { name: "Pride and Prejudice", authorId: "2" },
            ]);
    }
}

const outFile = fs.createWriteStream('...out-put-path...');

function saveToFile(fileName, data) {
    outFile.write(`${fileName}: ${data}\n`);

    return Promise.resolve();
}


function processData() {
    const authors = getFromURL('https://myapp.com/authors').then(author => {
        return authors;
    });

    const books = getFromURL('https://myapp.com/authors').then(books => {
        return books.sort();
    });

    return saveToFile('mysortedbooks.json', JSON.stringify(books)).then(() => {
        const authorAndBooks = authors.map(author => {
            var jsonData = {};
            jsonData['name'] = author.name;
            jsonData['books'] = [];
            for(var i=0; i<books.length; i++) {
                if(authod.id == books[i].authorId) {
                    jsonData['books'].push(books[i].name);
                }
            }
        });

        saveToFile('authorBooks.json', authorAndBooks);
    });
}

processData().then(() => outFile.end());

The main logic I have to implement is in processData method.

I tried adding code to solve the requirement but I got stuck how to return promise after all the operations. Also how to build my authorAndBooks JSON content.

Please help me with this.

const authors = getFromURL('https://myapp.com/authors').then(author => {
    return authors;
});

const books = getFromURL('https://myapp.com/authors').then(books => {
    return books.sort();
});

//authors and books are both promises here, so await them
return Promise.all([authors, books]).then(function(results){
    authors = results[0];
    books = results[1];
    return saveToFile(...);
});

alternatively declare your function async and do

const authors = await getFromURL('https://myapp.com/authors').then(author => {
    return authors;
});

const books = await getFromURL('https://myapp.com/authors').then(books => {
    return books.sort();
});

return await saveToFile(...);

Refactored code with Promise Chaining and to create multiple file streams

const fs = require('fs');

function getFromURL(url) {
    switch (url) {
        case 'https://myapp.com/authors':
            return Promise.resolve([
                { name: "Chinua Achebe", id: "1" },
                { name: "Hans Christian Andersen", id: "2" },
                { name: "Dante Alighieri", id: "3" },
            ]);
        case 'https://myapp.com/books':
            return Promise.resolve([
                { name: "Things Fall Apart", authorId: "1" },
                { name: "The Epic Of Gilgamesh", authorId: "1" },
                { name: "Fairy tales", authorId: "2" },
                { name: "The Divine Comedy", authorId: "2" },
                { name: "One Thousand and One Nights", authorId: "1" },
                { name: "Pride and Prejudice", authorId: "2" },
            ]);
    }
}

function saveToFile(fileName, data) {
    const outFile = fs.createWriteStream(`/var/${fileName}`);
    outFile.write(data);
    return Promise.resolve(outFile);
}

function authorBookMapping(data) {
    let [authors, books] = data;
    var jsonData = {};            
    authors.map(author => {
        jsonData['name'] = author.name;
        jsonData['books'] = [];
        for(var i=0; i<books.length; i++) {
            if(author.id == books[i].authorId) {
                jsonData['books'].push(books[i].name);
            }
        }
    });
    return {
        books: books,
        authorAndBooks: jsonData
    };  
}

function writeFile(data) {
    if(data) {
        const {books, authorAndBooks} = data;
        const book = saveToFile('mysortedbooks.json', JSON.stringify(books));
        const author = saveToFile('authorBooks.json', JSON.stringify(authorAndBooks));
        return Promise.all([book, author]);
    }    
}


function processData() {            

        const authors = getFromURL('https://myapp.com/authors');

        const books = getFromURL('https://myapp.com/authors');

        return Promise.all([authors, books])
                .then(authorBookMapping)
                .then(writeFile)                
}

processData().then((stream) => {
    for(let s in stream) {
        stream[s].close();
    }
})
.catch((err) => {
    console.log("Err :", err);
}) ;

lot of mistakes in your code. I will try to explain one by one, read comments between code. I would recommend you to read some basics of file operations and promises. Problem is in your saveToFile method and how you are chaining promises in processData method.

Change your saveToFIle function as following. You can also use promise supporting fs libraries like fs-extra but I'm not sure if you want to use an external library.

const path = require('path');     
const basePath = '.';//whatever base path of your directories    
function saveToFile(fileName, data) {
// fs.writeFile method uses callback, you can use many ways to convert a callback method to support promises
// this is one of the simple and doesn't require any libraries to import
   return new Promise((resolve,reject)=>{
        let filePath = path.join(basePath,fileName);
        return fs.writeFile(filePath,data,(err, data)=>{
            if(err) reject(err);
          else resolve();
        });
    })
}

Now change your processData function to use promise.all and to sort boooks in right way

function processData() {
    let books, authors;
    //Promise.all can be used when operations are not interdependent, fteches result fasetr as works like parallel requests
    return Promise.all([
        getFromURL('https://myapp.com/books'),
        getFromURL('https://myapp.com/authors')
    ]).then(data => {
        books = data[0];
        authors = data[1];
        let authorAndBooks = authors.map(author => {
            let jsonData = {};
            jsonData['name'] = author.name;
            jsonData['books'] = [];
            for(var i=0; i<books.length; i++) {
                if(author.id == books[i].authorId) {
                    jsonData['books'].push(books[i].name);
                }
            }
            return jsonData;
            console.log(jsonData);
        });
         // you will have to use a comparator to sort objects, given below it will sort books based on names.
         books.sort((first,second)=>{ return first.name>second.name ?1:-1})
        return Promise.all([
        saveToFile("mysortedbooks.json",JSON.stringify(books)),
        saveToFile("authorBooks.json",JSON.stringify(authorAndBooks))])
    }).then(data=>{
        console.log('All operations complete');
    }) 
}

processData();

Have you considered looking at this in a different way? If this is going to be the case for other APIs I'd think about aggregating those APIs in an aggregator service or the API itself if you can.

It is always better to receive all the data you need at once rather than multiple calls, you will incur latency and complexity.

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