简体   繁体   English

在nodejs中进行多次调用

[英]Make multiple calls in nodejs

I have some mock data for below 2 URLS: 我有一些以下2个URL的模拟数据:

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 现在我的任务是按名称对Books进行排序,并将排序后的列表作为JSON写入mysortedbooks.json文件

Then I have to create an array of authors with books property that has all the books of that author. 然后我必须创建一个具有books属性的作者数组,其中包含该作者的所有书籍。

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. 此情况不需要排序,数据应作为JSON存储在文件authorBooks.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. 例如,我应该在下面的代码中返回最后的saveToFile调用。

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. 我必须实现的主要逻辑是processData方法。

I tried adding code to solve the requirement but I got stuck how to return promise after all the operations. 我尝试添加代码来解决需求,但是在所有操作之后我遇到了如何返回promise的问题。 Also how to build my authorAndBooks JSON content. 另外如何构建我的authorAndBooks JSON内容。

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 或者声明你的函数async并做

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 使用Promise Chaining重构代码并创建多个文件流

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. 问题出在saveToFile方法中,以及如何在processData方法中链接promises。

Change your saveToFIle function as following. 更改saveToFIle函数,如下所示。 You can also use promise supporting fs libraries like fs-extra but I'm not sure if you want to use an external library. 您也可以使用支持fs库的promise,比如fs-extra,但我不确定您是否要使用外部库。

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 现在更改您的processData函数以使用promise.all并以正确的方式对boook进行排序

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. 如果其他API会出现这种情况,我会考虑在聚合器服务或API本身聚合这些API(如果可以的话)。

It is always better to receive all the data you need at once rather than multiple calls, you will incur latency and complexity. 最好一次接收所需的所有数据而不是多次呼叫,这将导致延迟和复杂性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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