Is it a bad code design if I put multiple calls to the same service in a single function? Each call to the service returns a value. Are there other better ways of implementing this?
For example:
$scope.getAdditionalInfo = () => {
ExampleService
.methodName($scope.parameter)
.then(function (res) {
//do things here
}, function (err) {
alert(err.message);
console.log(err);
});
ExampleService
.methodName2($scope.parameter)
.then(function (res) {
//do things here
}, function (err) {
alert(err.message);
console.log(err);
});
ExampleService
.methodName3($scope.parameter)
.then(function (res) {
//do things here
}, function (err) {
alert(err.message);
console.log(err);
});
}
Related
I am trying to figure out how to make the second Request run after the first request returned a response.I tried to search on google, but I'm kinda new to Postman and I'm not sure what should I look for.
I tried to do something like:
pm.sendRequest(req1, function(err, res) {
pm.sendRequest(req2, function(err, done)
)});
But It didnt work
while(documentLength>0)
{
pm.sendRequest(listDocumentsRequest, function(err, res){
pm.environment.set('dossierId',res.json().documentsList[index].subDossierId)
pm.environment.set('documentId',res.json().documentsList[index].documentId)
});
pm.sendRequest(getDocumentRequest);
index++;
documentLength--;
}
So I'm trying to make the first Request(listDocumentsRequest) then wait till I got an answer, then run the second request (getDocumentRequest) ,wait till I got an answer then move to the next iteration.
Do you guys have any idea?
Best Regards
Edited after Chilly answer
while(documentLength>0)
{
const interval = setTimeout(() => {}, Number.MAX_SAFE_INTEGER);
function resolvedPromise() {
return new Promise((resolve, reject) => {
pm.sendRequest(listDocumentsRequest, (err, res) => {
if (err) {
console.log(err);
reject();
} else {
pm.environment.set('dossierId',res.json().documentsList[index].subDossierId)
pm.environment.set('documentId',res.json().documentsList[index].documentId)
resolve();
}
});
});
}
resolvedPromise()
.then(pm.request(getDocumentRequest))
.then(() => clearTimeout(interval))
.catch(err => {
console.log(err);
clearTimeout(interval);
});
index++;
documentLength--;
}
// Example with a plain string URL
pm.sendRequest('https://postman-echo.com/get', (error, response) => {
if (error) {
console.log(error);
} else {
// Example with a plain string URL
pm.sendRequest('https://postman-echo.com/get/1', (error, response) => {
if (error) {
console.log(error);
} else {
console.log(response);
}
});
}
});
You can simply chain the requests what is the error you are facing? in your case it looks like
while (documentLength > 0) {
pm.sendRequest(listDocumentsRequest, function (err, res) {
pm.environment.set('dossierId', res.json().documentsList[index].subDossierId)
pm.environment.set('documentId', res.json().documentsList[index].documentId)
pm.sendRequest(getDocumentRequest, function (err, res) {});
index++;
documentLength--;
});
}
Assume I have the following scenario -
async.series(
[
function (cbi) {
students.getAll('student', function (err, response) {
if (err) {
logger.error(err);
}
cbi(err, response);
});
},
function (cbi) {
students.deleteAll('student', function (err, response) {
if (err) {
logger.error(err);
}
cbi(err, response);
});
},
function (cbi) {
teachers.getAll('teacher', function (err, response) {
if (err) {
logger.error(err);
}
cbi(err, response);
});
},
function (cbi) {
teachers.deleteAll('teacher', function (err, response) {
if (err) {
logger.error(err);
}
cbi(err, response);
});
};
]
);
And I want a graceful cleanup when SIGTERM is sent. That is a clean up of all the students or all teachers whichever is in progress when the signal was sent should complete and the next one should not begin.
function (cbi) {
students.getAll('student', function (err, response) {
if (err || GLOBAL_VAR_SIGTERM === true) {
logger.error(err);
}
cbi(err, response);
});
}
I was thinking that I should set a global variable to keep track of the SIGTERM signal.
process.on('SIGTERM', function onSigterm () {
GLOBAL_VAR_SIGTERM = true;
}
Is there any better way to break an async series to break a SIGTERM signal?
As #adamrights points out in his answer, the main problem in your code is you didn't call cbi(err, response) with a truthy err 1st param, which is essential to stop async.series continuing to next task in the queue.
Now your code should work, but you have a repeating pattern in your code that goes:
function (cbi) {
students.getAll('student', function (err, response) {
// these 3 lines appear in every callback function
if (GLOBAL_VAR_SIGTERM) err = new Error("SIGTERM: Aborting remaining tasks");
if (err) logger.error(err);
cbi(err, response);
// end of repeat pattern
});
}
Your callback passed to every async task always do the same 3-liner thing. We know the DRY rule, it's always a good idea to extract the repeating pattern into another function to reuse it as much as possible.
So instead of repeatedly declaring anonymous functions, you should declare a factory function.
function callbackFactory(cbi) {
return function(err, response) {
if (GLOBAL_VAR_SIGTERM) err = new Error("SIGTERM: Aborting remaining tasks");
if (err) logger.error(err);
cbi(err, response);
}
}
// use arrow function to write more concise code
async.series(
[
cbi => students.getAll('student', callbackFactory(cbi)),
cbi => students.deleteAll('student', callbackFactory(cbi)),
cbi => teachers.getAll('teacher', callbackFactory(cbi)),
cbi => teachers.deleteAll('teacher', callbackFactory(cbi)),
]
);
Advance Topic: use decorator to handle cross cutting concern
Let's explore this topic a bit more. Obviously, abort early on receiving SIGTERM is a cross cutting concern that should be separated from business logic. Suppose your business logic varies from task to task:
async.series(
[
cbi => students.getAll('student', (err, response) => {
if (err) {
logger.error(err);
return cbi(err);
}
updateStudentCount(response.data.length) // <- extra work
cbi(err, response);
}),
cbi => teachers.getAll('student', (err, response) => {
if (err) {
logger.error(err);
return cbi(err);
}
updateTeacherCount(response.data.length) // <- different extra work
cbi(err, response);
})
]
);
Because the callback is varying, it can be difficult to be extracted into a factory function like before. From this perspective, we'd better inject the abort-early behavior to each task, keep it easy to write normal business logic.
This is where decorator pattern comes handy. But global variable isn't the best tool to implement it, we'll use event listener.
The basic interface of the decorator looks like:
// `task` will be things like `cbi => students.getAll('student', ... )`
function decorateTaskAbortEarly(task) {
return (originalCbi) => {
...
task(originalCbi)
}
}
Below is our implementation checklist:
we're going to call originalCbi if we receive SIGTERM
but when we don't receive SIGTERM, the originalCbi is still callable inside callback of any async task like normal
if originalCbi is ever called once, we should unsubscribe from SIGTERM to prevent memory leak
The implementation:
function decorateTaskAbortEarly(task) {
return (originalCbi) => {
// subscribe to `SIGTERM`
var listener = () => originalCbi(new Error("SIGTERM: Aborting remaining tasks"));
process.once('SIGTERM', listener);
var wrappedCbi = (err, response) => {
// unsubscribe if `cbi` is called once
process.off('SIGTERM', listener);
return originalCbi(err, response);
};
// pass `cbi` through to `task`
task(wrappedCbi);
}
}
// Usage:
async.series(
[
cbi => students.getAll('student', (err, response) => {
if (err) {
logger.error(err);
return cbi(err);
}
updateStudentCount(response.data.length)
cbi(err, response);
}),
cbi => teachers.getAll('student', (err, response) => {
if (err) {
logger.error(err);
return cbi(err);
}
updateTeacherCount(response.data.length)
cbi(err, response);
})
].map(decorateTaskAbortEarly) // <--- nice API
);
If you want to respond to the SIGTERM event from within the async.series() then you are correct, the easiest way would be to track with a global variable.
But you need to set the first parameter (error-first callback) in the cbi(err, response) function to true in order to break the series.
so:
if (err || GLOBAL_VAR_SIGTERM === true) {
logger.error(err);
}
should be more like:
if (err) logger.error(err);
if (GLOBAL_VAR_SIGTERM) err = new Error("SIGTERM: Aborting remaining tasks");
// You could just do err = true
// But best practice is to use an Error instance.
Then because cbi(err, response) will be called with err value equal totrue the remaining tasks will not be run.
I liked the other answers. This is another way to achieve the same. I am using my own example:
var async = require('async');
var ifAsync = require('if-async')
var GLOBAL_VAR_SIGTERM = false;
async.series({
one: ifAsync(notsigterm).then(function (callback) {
setTimeout(function () {
console.log('one');
callback(null, 1);
}, 1000);
}),
two: ifAsync(notsigterm).then(function (callback) {
setTimeout(function () {
console.log('two');
callback(null, 2);
}, 1000);
}),
three: ifAsync(notsigterm).then(function (callback) {
setTimeout(function () {
console.log('three');
callback(null, 3);
}, 1000);
}),
four: ifAsync(notsigterm).then(function (callback) {
setTimeout(function () {
console.log('four');
callback(null, 4);
}, 1000);
}),
}, function (err, results) {
if (err) {
//Handle the error in some way. Here we simply throw it
//Other options: pass it on to an outer callback, log it etc.
throw err;
}
console.log('Results are ' + JSON.stringify(results));
});
process.on('SIGTERM', function onSigterm () {
console.log('SIGTERM caught');
GLOBAL_VAR_SIGTERM = true;
});
function notsigterm(callback) {
if (!GLOBAL_VAR_SIGTERM) return callback(null, true)
else return callback(null, false)
}
I am using a package called ifAsync which allows you to use a predicate notsigterm to decide if a callback should be invoked or not. If notsigterm returns true then the callback will be invoked else it will be skipped. This is similar answer to others but somehow I find this cleaner. Let me know if you have questions.
i am trying to perform a simple action like upload a file to dropbox,
the file is upload succsfully
what i need is the returned answer that conatain file name,size,path etc.
i know that i lost in the async calls,
and i would like to get some help here please:
exports.uploadFile = async function () {
fs.readFile('./text.txt', function (err, contents) {
if (err) {
console.log('Error: ', err);
}
uploadFile(contents);
});
} ;
async function uploadFile(fileCont) {
let dbx = new Dropbox({ accessToken: APP_KEY });
await dbx.filesUpload({ path: '/basic4.txt', contents: fileCont })
.then(function (response) {
console.log( response);
return response;
})
.catch(function (err) {
console.log(err);
});
}
and i wanted to return the result to fron and so i used this part:
DriveService.uploadFile()
.then((success)=>{
return res.status(200).json({success:true,data:success,message:'list of files recived'});
})
.catch((error)=>{
return res.status(400).json({success:false,data:{},message:error.message});
})
the problem is that the success is always empty since i got lost in the async forest.
can somone please advise?
Thanks
Not sure bout solution in async but, You can use callback like this:
exports.uploadFile = async function (cb) {
fs.readFile('./text.txt', function (err, contents) {
if (err) {
console.log('Error: ', err);
}
uploadFile(contents,cb);
});
} ;
async function uploadFile(fileCont,cb) {
let dbx = new Dropbox({ accessToken: APP_KEY });
await dbx.filesUpload({ path: '/basic4.txt', contents: fileCont })
.then(function (response) {
console.log( response);
cb(response);//Pass response in callback
})
.catch(function (err) {
console.log(err);
});
}
DriveService.uploadFile(function(success) {//this callback will be called from async
return res.status(200).json({success:true,data:success,message:'list of files recived')
})
.catch((error)=>{
return res.status(400).json({success:false,data:{},message:error.message});
})
Given the following array of values:
var sportList = ['football', 'volleyball'];
i want to run a query on mongo database using each of these values:
function myFunc(sport, callback) {
mongoDB.sports.find({'name': sport}, function (error, result) {
if (error) {
callback(error)
} else {
callback(null, result)
}
})
}
so i build my promises like:
var promises = sportList.map(function(val){
return myFunc(val);
});
and then trying to run all in a promise all chain:
Promise.all(promises)
.then(function (result) {
console.log('log results: ', result);
})
.catch(function (error) {
console.log(error);
});
but this is not working, because it is complaining that the callback is undefined, how can i fix this up correctly?
The reason for the error is that you are calling the myFunc method without supplying the callback parameter.
A solution would be to replace the myFunc function with the below. This function will return a new Promise. I haven't tested the below code but it should work.
function myFunc(sport) {
return new Promise((resolve, reject) => {
mongoDB.sports.find({'name': sport}, function (error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
})
}));
}
I want to have three functions that do something like this:
function getuser1(){
var user = b();
//do some stuff to user
return user;
}
function getuser2(){
var user = asyncfinduser();
//do some different stuff to user
return user;
}
function asyncfinduser(){
userDB.find( { /* some criteria */ }, function (error, user) {
//return found user to functions above
return user;
});
)
The above is obviously not going to work so how can I fix this? Thank you
Since you cannot return synchronously from async callbacks, you will need to use callbacks:
function getuser1(callback) {
asyncfinduser(function(error, user) {
if (error) return callback(error);
//do some stuff to user
callback(null, user);
});
}
function getuser2(callback) {
asyncfinduser(function(error, user) {
if (error) return callback(error);
//do some different stuff to user
callback(null, user);
});
}
function asyncfinduser(callback) {
userDB.find( { /* some criteria */ }, function (error, user) {
if (error) return callback(error);
//return found user to functions above
callback(null, user);
});
}
However, you might be able to apply the promise pattern:
var Promise = …; // some library
function getuser1() {
return asyncfinduser().then(function(user) {
//do some stuff to user
return user;
});
}
function getuser2() {
return asyncfinduser().then(function(user) {
//do some different stuff to user
return user;
});
}
function asyncfinduser() {
return new Promise(function(resolve, reject) {
userDB.find( { /* some criteria */ }, function (error, user) {
if (error) reject(error);
else resolve(user); //return found user to functions above
});
}