How to return values from async functions using async await from a callback function?

Viewed 5141

I'm new to nodejs and it's callback hell, I read about async / await introduction in node 8 and was interested to implement it that way

I have a specific set of methods that I need to call in a synchronous manner one after another for trello API e.g

  1. create board
  2. create labels using board id
  3. create cards using board id
  4. attach labels to card
  5. create list in card
  6. add each item to list in a card

you can imagine in nodejs, this requires significant callbacks nested into one another to access the previous object

createProjectBoard: function (project) {
        t.post("1/board", {
            name: project.name,
            desc: project.description,
            defaultLists: false
        }, function (err, board) {
            if (err) {
                console.log(err);
                throw err;
            }

            //get board id from data
            let boardId = board.id
            let backlogListId = "";
            let highId = "", mediumId = "", lowId = "";

            //create labels
            t.post("1/labels", {
                name: 'High',
                color: 'red',
                idBoard: boardId
            }, function (err, label) {
                console.log(err || 'High label created');
                if (err) return;
                highId = label.id;
            });

            t.post("1/labels", {
                name: 'Medium',
                color: 'orange',
                idBoard: boardId
            }, function (err, label) {
                console.log(err || 'Medium label created');
                if (err) return;
                mediumId = label.id;
            });

            t.post("1/labels", {
                name: 'Low',
                color: 'yellow',
                idBoard: boardId
            }, function (err, label) {
                console.log(err || 'Low label created');
                if (err) return;
                lowId = label.id;
            });

            //create rest of the lists
            t.post("1/lists", { name: "Completed", idBoard: boardId }, function (e, l) {
                if (e) {
                    console.log(e);
                    return;
                }
                console.log(l);
                t.post("1/lists", { name: "Testing", idBoard: boardId }, function (e, l) {
                    if (e) {
                        console.log(e);
                        return;
                    }
                    console.log(l);
                    t.post("1/lists", { name: "In Progress", idBoard: boardId }, function (e, l) {
                        if (e) {
                            console.log(e);
                            return;
                        }
                        console.log(l);

                        //create backlog list
                        t.post("1/lists", { name: "Backlog", idBoard: boardId }, function (e, list) {
                            if (e) {
                                console.log(e);
                                return;
                            }
                            console.log(list);
                            backlogListId = list.id;
                            console.log("backlog card list id:" + backlogListId);

                            _.each(project.userStories, function (story) {
                                //assign labels
                                let labelId = "";
                                switch (story.complexity.toLowerCase()) {
                                    case 'high':
                                        labelId = highId;
                                        break;
                                    case 'medium':
                                        labelId = mediumId;
                                        break;
                                    default:
                                        labelId = lowId;
                                }

                                t.post("1/cards", {
                                    name: story.title,
                                    idLabels: labelId,
                                    idList: backlogListId
                                }, function (e, card) {
                                    if (e) {
                                        console.log(e);
                                        return;
                                    }
                                    let cardId = card.id;
                                    console.log("created id:" + cardId + ";card:" + story.title);                                    

                                    t.post("1/cards/" + cardId + "/checklists", {
                                        name: "Acceptance Criteria"
                                    }, function (e, checklist) {
                                        if (e) {
                                            console.log(e);
                                            return;
                                        }
                                        console.log('checklist created:');
                                        var clId = checklist.id;
                                        _.each(story.criterion, function (criteria) {
                                            t.post("1/cards/" + cardId + "/checklist/" + clId + "/checkItem", {
                                                name: criteria
                                            }, function (e, checkItem) {
                                                if (e) {
                                                    console.log(e);
                                                    return;
                                                }
                                                console.log('created check item:' + checkItem);
                                            });
                                        });
                                    });
                                });
                            });
                        });
                    });
                });
            });
        });
    }

I'm still having issues with the above code where __.each loop is involved, it calls all the functions in the loop asynchronously (re-arranging the order of items in which they were supposed to be originally) - so I thought that there has to be a better way to make the calls synchronously

I'm interested in using await / async to clean out the code , but running into some trouble on returning the object from the async callback

the solution is based in sails.js, the following is an excerpt from the TrelloService I'm writing

consider the following:

 createProjectBoard: async function(project) {
        //get board id from data
        let board;
        let boardId = "";
        let backlogListId = "";
        let highId = "",
            mediumId = "",
            lowId = "";


        try {
            await t.post("1/board", {
                    name: project.name,
                    desc: project.description,
                    defaultLists: false
                },
                function(err, b) {
                    if (err) {
                        console.log(err);
                        throw err;
                    }
                    console.log("board" + b);
                    board = b;
                });

            //create labels
            await t.post("1/labels", {
                name: 'High',
                color: 'red',
                idBoard: board.id
            }, function(err, label) {
                console.log(err || 'High label created');
                if (err) return;
                highId = label.id;
            });

        } catch (err) {
            console.log(err);
        }
}

I need the board value to be available in the labels request call, so far I'm unable to retrieve the board object, event though I've setup await keywords

I need to be able to get objects from call back functions and use them into subsequent function calls in a synchronous manner

I'm using a trello api wrapper node-trello to make the calls (t)

One way would be wrap the above in more functions with a callback as follows, but I don't think that'd be best practice, as I'd have to write wrapper callbacks on every object that I need to use

function foo(url,options,cb){
await t.post(url, options,
        function(err, b) {
            if (err) {
                console.log(err);
                throw err;
            }
            console.log("board" + b);
            cb(b);
        });
}

var url = "1/board";
var options = {
            name: project.name,
            desc: project.description,
            defaultLists: false
        };

foo(url,options,function(board){
   console.log(board); //board object
});

any suggestions are appreciated

2 Answers
Related