How to handle callback function in javascript? - javascript

The async function itself should make use of the lookup() function what I have used inside the async function,but return the result inside the callback.
The parameters for the callback are err and res.
If an Error has been thrown by lookup() then it should be passed to
err, otherwise err is null or undefined.If a result has been returned by lookup() then it should be passed to res, otherwise res is null or undefined,I have other two tests for checking property as like user but I have shortened the code as much as possible. the problem is callback inside lookupAsync() function.
const users = [
{
"login": "norvig",
"firstName": "Peter",
"lastName": "Norvig",
"likes": ["AI", "Search", "NASA", "Mars"]
}
];
// lookupAsync()
const lookupAsync = (login, prop, callback) => {
// Only change code below this line
const found = users.find(function(e) {
return e.login === login;
});
if (!found) {
throw "Could not find user.";
} else {
if (prop in found) {
return found[prop];
} else {
throw "Could not find property";
}
}
//my current concept according to suggestion but trying to set in the
code.
function mycallback(callback) {
var err,res;
callback(err,res);
}
mycallback( function() {
console.log();
});
};
test('lookupAsync() likes', assert => {
const msg = `lookupAsync(<login>, 'likes', callback) should return
likes for the specified user.`;
lookupAsync('norvig', 'likes', function(err, res){
const actual = res;
const expected = ["AI", "Search", "NASA", "Mars"];
assert.deepEqual(actual, expected, msg);
assert.end();
});
});
test('lookupAsync() with unknown user', assert => {
const msg = `lookupAsync() with unknown user should return an error
with the correct message.`;
const value = lookupAsync('nobody', 'likes', function(err, res){
const actual = err.message;
const expected = 'Could not find user.';
assert.equal(actual, expected, msg);
assert.end();
});
});

Let me do it for you
const lookupAsync = (login, prop, callback) => {
const found = users.find(function(e) {
return e.login === login;
});
if (!found) {
callback(new Error("Could not find user."));
} else {
if (prop in found) {
callback(null, found[prop]);
} else {
callback(new Error("Could not find property"));
}
}
}

Related

Async.js "Callback was already called"; can't debug

I am using async.js to help with validation and transformations for data imports. After moving a piece of code to it's own function I get the error "Callback was already called." from async.js. The stack trace has no useful information on debugging. The exact code works fine when I call it directly. I need to use it across multiple imports so that's why I moved to the an object containing other "transformers". They all work fine.
I've tried inserting the following code into the function to try to debug:
return cb("test);
It runs fine and returns the error "test" to the browser if I add it to the first line of the function. Inserting it anywhere else causes the error.
The following works. If I move "return cb("error);" to the line after let query = ... it will fail with the callback error. I'm quite confused.
user(options = {
select: "id",
return: "id",
lean: true,
}) {
return (value, cb) => {
if (!value) {
return cb(null, null);
}
return cb("test");
let query = User.findOne({
$or: [{
email: value.toLowerCase()
},
{
username: value
},
]
}).select(options.select);
if (options.lean) {
query.lean();
}
query.exec((error, user) => {
if (error) {
return cb(error);
}
if (!user) {
return cb(`user not found: ${value}`);
}
if (options.return == "object") {
cb(null, user);
} else {
cb(null, user._id);
}
});
}
}
The variable "cb" is passed from an async call:
async.mapValues(self.options.validators, (validators, key, cb) => {
//Get the value that is being validated
var value = _.get(document, key);
var isArray = false;
//Enforce array type
if (!Array.isArray(validators)) {
validators = [validators];
}
//Transform the value before validating it
var transformer = _.get(self.options.transformers, key);
//Don't run transformer if empty value
if (!transformer || value == null) {
transformer = (value, cb) => cb(null, value);
}
transformer(value, (error, newValue) => {
//Check error
if (error) {
return _cb(error);
}
//Set new value
value = newValue;
_.set(document, key, newValue);

Express dosn't get return of other function querying Mongodb [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I'm working in a simple API Key authentication, I just want to verify the given key against the user provied key.
I have a seperate file with the function querying the database, and returning true/false and the user object.
But in my route.js file, the return object is undefined even tough in my auth.js file it isn't.
I tried making the the function in router.get an async function using express-promise-router and making the function an await return var user = await auth.verify(req.params.uid, req.get("token")) but I don't realy know how async works.
router.js
[...]
router.get('/list/:uid', function(req, res) {
var user = auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user) // <-- Undefined
if (user.status) {
res.send("Success")
} else {
res.status(403)
res.json({status: 403, error: "Unkown User / Token"})
}
})
[...]
auth.js
var db = require('./db')
var ObjectId = require('mongodb').ObjectId;
module.exports = {
verify: (uid, key) => {
try {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {throw err}else{status=1}
if (user.length <= 0) {throw "NotExistingExc"; status = 0}else{
usr = user[0];
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
} /* --> Is {
status: 1,
user: {
_id: d47a2b30b3d2770606942bf0,
name: 'Sh4dow',
groups: [ 0 ],
api: 'YWFiMDI1MGE4NjAyZTg0MWE3N2U0M2I1NzEzZGE1YjE='
}
}
*/
return returnObj;
})
} catch (e) {
console.error(e)
return {
status: 0,
user: {},
error: e
}
}
}
}
db.js (Idk if needed)
var MongoClient = require('mongodb').MongoClient
var state = {
db: null,
}
exports.connect = function(url, done) {
if (state.db) return done()
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) return done(err)
state.db = db
done()
})
}
exports.get = function() {
return state.db.db("database")
}
exports.close = function(done) {
if (state.db) {
state.db.close(function(err, result) {
state.db = null
state.mode = null
done(err)
})
}
}
I want to have the returnObjin auth.js in the router.get of my route.js file.
Make auth.verify return a Promise which we can then await for it inside router, You can just make the callback async no need for express-promise-router
router.get('/list/:uid', async function(req, res) {
try {
var user = await auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user)
if (user.status) {
res.send("Success")
} else {
res.status(403).json({status: 403, error: "Unkown User / Token"})
}
} catch (e) {
console.error(e)
res.status(/* */).json(/* */)
}
})
auth
module.exports = {
verify: (uid, key) => new Promise((resolve, reject) => {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {
reject(err)
return
} else {
status = 1
}
if (user.length <= 0) {
reject(new Error("NotExistingExc"))
return
} else {
usr = user[0]
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
}
resolve(returnObj);
})
}
}
In short, the reason you get undefined is because the code in auth.js is asyncronous. But you're really close. The toArray method in MongoDB returns a promise, so you need to make sure you return that promise and then use it in the router correctly.
In auth.js, make sure verify returns a promise - just add return!
return collection.find(query).limit(1).toArray(...)
And then, change your usage of the verify to the async/await you originally tried:
router.get('/list/:uid', async function(req, res) {
var user = await auth.verify(req.params.uid, req.get("token"))
// More code here...
})

Firebase Functions response returns null

tried get list of objects from s3(actually from wasabi) but in react always returns null
function code:
(using node 8)
exports.fetchWasabi = functions.https.onCall(() => {
const params =
{
Bucket: 'balde1-webcars',
};
s3.listObjectsV2(params, function(err, result) {
if (!err) {
console.log(result)
return result
} else {
console.log(err);
return err
}
});
});
react code:
componentDidMount(){
var Wasabi = firebase.functions().httpsCallable('fetchWasabi');
Wasabi().then(function(result) {
var res = result.data;
console.log(res)
}).catch(function(error) {
console.log(error)
});
}
when i check the log for the function i can see the result but it never reaches my browser apparently
You are not returning anything so you should add one more return like :
return s3.listObjectsV2(params, function(err, result) { ...

Callback NodeJS & insert data with mongoose

I know this topic as already asked many times before but I didn't find the right answer to do what I want.
Actually, I try to save two different list of JSON object in MongoDB via Mongoose. To perform both at the same time I use 'async'.
However, when I save it with the command insertMany() I get an error because he calls the callback of async before finishing the insertMany(). Therefore answer[0] is not defined.
What will be the proper way of doing it ?
Here is my code with the async:
const mongoose = require("mongoose");
const async = require("async");
const utils = require("../utils");
const experimentCreate = function(req, res) {
let resData = {};
let experimentList = req.body.experiment;
let datasetList = req.body.datasetList;
async.parallel(
{
dataset: function(callback) {
setTimeout(function() {
answer = utils.createDataset(datasetList);
callback(answer[0], answer[1]);
}, 100);
},
experiment: function(callback) {
setTimeout(function() {
answer = utils.createExp(experimentList);
callback(answer[0], answer[1]);
}, 100);
}
},
function(err, result) {
if (err) {
console.log("Error dataset or metadata creation: " + err);
sendJSONresponse(res, 404, err);
} else {
console.log("Experiment created.");
resData.push(result.dataset);
resData.push(result.experiment);
console.log(resData);
sendJSONresponse(res, 200, resData);
}
}
);
};
Then the two functions called createExp and createDataset are the same in another file. Like this:
const createDataset = function(list) {
let datasetList = [];
for (item of list) {
let temp = {
_id: mongoose.Types.ObjectId(),
name: item.name,
description: item.description,
type: item.type,
};
datasetList.push(temp);
}
Dataset.insertMany(datasetList, (err, ds) => {
if (err) {
console.log("Error dataset creation: " + err);
return [err, null];
} else {
console.log("All dataset created.");
return [null, ds];
}
});
};
There's a few problems with your code. For one, you're not returning anything in your createDataset function. You're returning a value in the callback of insertMany but it doesn't return that value to the caller of createDataset as it's within another scope. To solve this issue, you can wrap your Dataset.insertMany in a promise, and resolve or reject depending on the result of Data.insertMany like this:
const createDataset = function(list) {
let datasetList = [];
for (item of list) {
let temp = {
_id: mongoose.Types.ObjectId(),
name: item.name,
description: item.description,
type: item.type,
};
datasetList.push(temp);
}
return new Promise((resolve, reject) => {
Dataset.insertMany(datasetList, (err, ds) => {
if (err) {
console.log("Error dataset creation: " + err);
reject(err);
} else {
console.log("All dataset created.");
resolve(ds);
}
});
});
};
Now your return object is no longer going to be an array so you won't be able to access both the error and the result via answer[0] and answer[1]. You're going to need to chain a then call after you call createDataset and use callback(null, answer) in the then call (as that means createDataset executed successfully) or use callback(err) if createDataset throws an error like below:
dataset: function(callback) {
setTimeout(function() {
utils.createDataset(datasetList).then(answer => {
callback(null, answer);
}).catch(err => callback(err)); // handle error here);
}, 100);
}
Note: You'll most likely need to alter your createExp code to be structurally similar to what I've produced above if it's also utilizing asynchronous functions.

Find one or create with Mongoose

I have
Page.findById(pageId).then(page => {
const pageId = page.id;
..
});
My problem is that if no page id is given, it should just take the first available page given some conditions, which is done by
Page.findOne({}).then(page => {
const pageId = page.id;
..
});
but if no page is found, it should create a new page and use this, which is done with
Page.create({}).then(page => {
const pageId = page.id;
..
});
But how do I combine all this to as few lines as possible?
I have a lot of logic going on inside
page => { ... }
so I would very much like to do this smart, so I can avoid doing it like this
if (pageId) {
Page.findById(pageId).then(page => {
const pageId = page.id;
..
});
} else {
Page.findOne({}).then(page => {
if (page) {
const pageId = page.id;
..
} else {
Page.create({}).then(page => {
const pageId = page.id;
..
});
}
});
}
I am thinking I maybe could assign a static to the schema with something like
pageSchema.statics.findOneOrCreate = function (condition, doc, callback) {
const self = this;
self.findOne(condition).then(callback).catch((err, result) => {
self.create(doc).then(callback);
});
};
As per the Mongoose docs:
As per previous SO answer
Model.findByIdAndUpdate()
"Finds a matching document, updates it according to the update arg, passing any options, and returns the found document (if any) to the callback."
In the options set upsert to true:
upsert: bool - creates the object if it doesn't exist. defaults to false.
Model.findByIdAndUpdate(id, { $set: { name: 'SOME_VALUE' }}, { upsert: true }, callback)
Related to Yosvel Quintero's answer which didn't work for me:
pageSchema.statics.findOneOrCreate = function findOneOrCreate(condition, callback) {
const self = this
self.findOne(condition, (err, result) => {
return result ? callback(err, result) : self.create(condition, (err, result) => { return callback(err, result) })
})
}
And then use it like:
Page.findOneOrCreate({ key: 'value' }, (err, page) => {
// ... code
console.log(page)
})
Promise async/await version.
Page.static('findOneOrCreate', async function findOneOrCreate(condition, doc) {
const one = await this.findOne(condition);
return one || this.create(doc);
});
Usage
Page.findOneOrCreate({ id: page.id }, page).then(...).catch(...)
Or
async () => {
const yourPage = await Page.findOneOrCreate({ id: page.id }, page);
}
Each Schema can define instance and static methods for its model. Statics are pretty much the same as methods but allow for defining functions that exist directly on your Model
Static method findOneOrCreate:
pageSchema.statics.findOneOrCreate = function findOneOrCreate(condition, doc, callback) {
const self = this;
self.findOne(condition, (err, result) => {
return result
? callback(err, result)
: self.create(doc, (err, result) => {
return callback(err, result);
});
});
};
Now when you have an instance of Page you can call findOneOrCreate:
Page.findOneOrCreate({id: 'somePageId'}, (err, page) => {
console.log(page);
});
One lines solution with async/await:
const page = Page.findOne({}).then(p => p || p.create({})
If you don't want to add a static method to the model, you can try to move some things around and at least not to have all these callback nested levels:
function getPageById (callback) {
Page.findById(pageId).then(page => {
return callback(null, page);
});
}
function getFirstPage(callback) {
Page.findOne({}).then(page => {
if (page) {
return callback(null, page);
}
return callback();
});
}
let retrievePage = getFirstPage;
if (pageId) {
retrievePage = getPageById;
}
retrievePage(function (err, page) {
if (err) {
// #todo: handle the error
}
if (page && page.id) {
pageId = page.id;
} else {
Page.create({}).then(page => {
pageId = page.id;
});
}
});
The solutions posted here ignore that this pattern is most common when there's a unique index on a field or a combination of fields. This solution considers unique index violation errors correctly:
mongoose.plugin((schema) => {
schema.statics.findOrCreate = async function findOrCreate(key, attrs) {
try {
return await this.create({ ...attrs, ...key });
} catch (error) {
const isDuplicateOnThisKey =
error.code === 11000 &&
Object.keys(error.keyPattern).sort().join(',') ===
Object.keys(key).sort().join(',');
if (isDuplicateOnThisKey) {
const doc = await this.findOne(error.keyValue);
doc.set(attrs);
return await doc.save();
}
throw error;
}
};
});
Usage:
await Post.findOrCreate({ slug: 'foobar' }, { title: 'Foo Bar', body });
try this..
var myfunc = function (pageId) {
// check for pageId passed or not
var newId = (typeof pageId == 'undefined') ? {} : {_id:pageId};
Page.findOne(pageId).then(page => {
if (page)
const pageId = page.id;
else { // if record not found, create new
Page.create({}).then(page => {
const pageId = page.id;
});
}
});
}

Categories