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
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
Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise. Note: Even though the return value of an async function behaves as if it's wrapped in a Promise.resolve , they are not equivalent.
The await keyword is used in an async function to ensure that all promises returned in the async function are synchronized, ie. they wait for each other. Await eliminates the use of callbacks in .
The keyword await is used to wait for a Promise. It can only be used inside an async function. This keyword makes JavaScript wait until that promise settles and returns its result.
Inside an async function, you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.
I'm interested in using await / async to clean out the code
There are two steps to this: promisification and async. Your code is getting confused because it's skipping the first step.
Promisification creates very simple promise-returning wrapper functions around the callback functions. For example, if t
is an instance of a Trello
class:
Trello.prototype.postAsync = (url, data) => new Promise((resolve, reject) => {
this.post(url, data, (err, result) => {
if (err) { reject(err); }
else { resolve(result); }
});
});
The second step is to write your async
/await
logic, using promise-returning functions and not callbacks. Since they are promise-returning, they code is much more natural:
const board = await t.postAsync("1/board", {
name: project.name,
desc: project.description,
defaultLists: false
});
console.log("board" + board);
//create labels
let highId;
try {
highId = await t.postAsync("1/labels", {
name: 'High',
color: 'red',
idBoard: board.id
});
} catch (err) {
console.log(err || 'High label created');
return;
}
The promisification step is tedious and repetitive. There are some libraries that can automate callbacks-to-promises, most notably Bluebird.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With