Im playing arround with promises and callbacks and wonder what is the correct way to write a function that returns a promise if no callback is passed.
My result looks like this, but im not sure if this is correct (in the meaning of anti pattern)
const mySuperFunction = function mySuperFunction(data, cb) {
let wrapper = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() >= 0.5) {
resolve(Date.now());
} else {
reject(new Error("Not today..."));
}
}, 100);
});
if (cb) {
wrapper.then((result) => {
cb(null, result);
}, (error) => {
cb(error);
});
} else {
return wrapper;
}
};
mySuperFunction().then((time) => {
console.log(time)
}).catch((err) => {
console.log(err);
});
mySuperFunction(null, (err, time) => {
console.log(err, time)
});
Its simple: create a function and wrap the "work" code in a promise.
If no callback is passed to my function, i return the wrapped promise. If a calback is passed, i wrap/call it from .then(...) and .catch(...)
Is this ok, or do i miss some special cases where this dosn't work ?
Is this ok, or do i miss some special cases where this dosn't work ?
Your code works, but it adds unuseful overhead. A promise consumes more memory than callback and it is slower than callbacks.
I would write something like
const { promisify } = require('util')
function mySuperFunctionCallback (data, cb) {
setTimeout(() => {
if (Math.random() >= 0.5) {
cb(Date.now())
} else {
cb(new Error('Not today...'))
}
}, 100)
}
const mySuperFunctionAsync = promisify(mySuperFunctionCallback)
function mySuperFunction (data, cb) {
if (cb) {
mySuperFunctionCallback(data, cb)
} else {
return mySuperFunctionAsync(data)
}
}
mySuperFunction().then((time) => {
console.log(time)
}).catch((err) => {
console.log(err)
})
mySuperFunction(null, (err, time) => {
console.log(err, time)
})
Related
according to what I asked yesterday: Taking a value from Promise and returning it instead of console.log
I still have a problem, because when I try to turn on the list of functions, the return results happen before it even gets into the functions. I can't use async/await, so that's why I struggle with this exercise.
Is there any way to:
If I console.log(fun1(1000,callback)) it doesn't return anything (undefined), but I would like it to return the value res, but not console.logging it, but just to make the callback return it.
The idea is to put into results those results, that the pred accepts, so e.g. greater than 5.
I know that the problem is with setTimeout probably, but as mentioned in the post I posted yesterday - I can't use async/await to fix that problem.
My code:
function fun1(value,cb) {
if ((value * 100) > 400) {
setTimeout(() => cb(null, value*100), 1000)
}
else {
setTimeout(() => cb(new Error("error")), 1000)
}
}
function fun2(value,cb) {
if ((value * 20) > 50) {
setTimeout(() => cb(null, value*20), 2000)
}
else {
setTimeout(() => cb(new Error("error")), 2000)
}
}
function fun3(value,cb) {
if ((value -30 ) > 0) {
setTimeout(() => cb(null, value-30), 3000)
}
else {
setTimeout(() => cb(new Error("error")), 3000)
}
}
function callback(err, res) {
if (err) {
console.log(err)
}
else {
console.log(res)
return res
}
}
console.log(fun1(1000,callback))
const together = (funcs, pred) => (n) => {
let results = []
funcs.reduce((prev,curr) => {
if (curr(n,callback) > pred) {
results.push(curr(n,callback))
}
}, 0)
return results
};
console.log(together([fun1,fun2,fun3], 5)(10))
In past people afraid to write async code because of callback, As so I, So I will use promise,
So that you need to return a Promise object from your function like:
function fun1(value) {
return new Promise((res, rej) => {
if ((value * 100) > 400)
setTimeout(() => res(value * 100), 1000)
else
setTimeout(() => rej(new Error("error")), 1000)
});
}
Instead of warping a Promise object, I took deferent approach, I wrote a utility function (callFuture) that call cl and return a Promise object for us.
function callFuture(fn, value, cl) {
return new Promise((res, rej) => {
fn(value, (...v) => {
let result = cl(...v);
if (result instanceof Error)
rej(result)
else
res(result)
})
});
}
function fun1(value, cb) {
if ((value * 100) > 400)
setTimeout(() => cb(null, value * 100), 1000)
else
setTimeout(() => cb(new Error("error")), 1000)
}
function fun2(value, cb) {
if ((value * 20) > 50)
setTimeout(() => cb(null, value * 20), 2000)
else
setTimeout(() => cb(new Error("error")), 2000)
}
function fun3(value, cb) {
if ((value - 30) > 0)
setTimeout(() => cb(null, value - 30), 3000)
else
setTimeout(() => cb(new Error("error")), 3000)
}
function callback(err, res) {
return err ? err : res
}
callFuture(fun1, 100, callback)
.then(console.log)
.catch(console.log)
async function together(funcs, pred, n) {
let results = [];
for (const fn of funcs) {
try {
let v = await callFuture(fn, n, callback);
if (v > pred)
results.push(v);
} catch (error) {
console.log(`error`)
}
}
return results;
}
together([fun1, fun2, fun3], 5, 10).then(console.log)
You can think await callFuture(fn, n, callback) is like synchronous (this is not true), There is no need to do anything to handle async stuff, it will return result or throw error (much like a sync function), Any you can handle error with try/catch as like you do in a sync function...
This works when use the callback function
Your Example Include the callback after timeout:
function fun1(value, calculate, callback) {
if (value * 100 > 400) {
setTimeout(() => {
let result = calculate(null, value * 100);
callback(result)
}, 1000);
} else {
setTimeout(() => cb(new Error("error")), 1000);
}
}
function calculate(err, res) {
if (err) {
console.log(err);
} else {
console.log(res);
return res;
}
}
fun1(1000, calculate, function(result) {
console.log(`this is the result in the callback after timeout ${result}`)
});
I need to call two async functions sequentially inside a new function. Both functions use callbacks to perform asynchronous operations.
When I just call my functions one by one I am faced with the problem that, as I understand it, they are called synchronously, and the second function does not wait for the first to execute
How can I wrap them to be performed sequentially?
Code that I have:
let firstFunction = (data, callback) => {
DBCallingFirst
.asyncMethodFirst(resultObj, (err, result) => {
if (err) return callback(err);
// Some code
});
}
let secondFunction = (data, callback) => {
DBCallingSecond
.asyncMethodSecond(resultObj, (err, result) => {
if (err) return callback(err);
// Some code
});
}
let newFunction = (data, callback) => {
firstFunction(data, callback);
secondFunction(data, callback);
}
Thanks for your attention!
If you are able to use promises, or async/await, you can make this a lot cleaner. As it is, you can just do the second db operation in the callback from the first one - so that guarantees the first one completes before calling the second
let firstFunction = async (data, callback) => {
DBCallingFirst
.asyncMethodFirst(resultObj, (err, result) => {
if (err) return callback(err);
DBCallingSecond
.asyncMethodSecond(resultObj, (err, result) => {
if (err) return callback(err);
// Some code
});
});
}
EDIT: With promises, you could structure it like this:
let firstFunction = (data) => {
return new Promise(() => {
DBCallingFirst
.asyncMethodFirst(data, (err, result) => {
if (err) {
return Promise.reject();
}
return Promise.resolve(result);
});
});
}
let secondFunction = (data) => {
return new Promise(() => {
DBCallingSecond
.asyncMethodSecond(data, (err, result) => {
if (err) {
return Promise.reject();
}
return Promise.resolve(result);
});
});
}
let newFunction = (data, callback) => {
firstFunction(data).then((result1) => {
secondFunction(data).then(result2) {
}).catch(err2) {
console.error(err2);
});
}.catch((err1) => {
console.error(err1);
});
}
Return Promise from both the functions and await them like this
const firstFunction = (data) => {
return new Promise((resolve, reject) => {
DBCallingFirst.asyncMethodFirst(data, (err, result) => {
if(err) reject(err);
resolve(result);
})
})
}
const secondFunction = (data) => {
return new Promise((resolve, reject) => {
DBCallingSecond.asyncMethodSecond(data, (err, result) => {
if(err) reject(err);
resolve(result);
})
})
}
Now you can call both of them sequentially using await
const newFunction = async (data) => { // note the async part in function definition
try {
const result1 = await firstFunction(data);
const result2 = await secondFunction(data); // this will be executed only after firstFunction resolves/rejects;
} catch (err) {
console.log('Error from the function', error) // promise rejection will be caught here
}
}
I want to create a function which can be utilised in 3 ways for creating npm dependency
Promise way
callback way
async/await way
For Example
1) async/await
var mongoose = require('mongoose');
async function Connection() {
try {
await mongoose.connect('mongourl');
} catch (err) {
console.error("Connection error --->", err);
}
}
Connection();
2) Callback Style
var mongoose = require('mongoose');
mongoose.connect('mongourl', function (err) {
if (err) console.error("Connection error --->", err);
});
3) Promise Style
var mongoose = require('mongoose');
mongoose.connect('mongourl').then(() => {
}).catch(err => console.error("Connection error --->", err));
Did u absorve that mongoose.connect is same name for all types
You can try with:
const connect = (name, callback) => {
try {
const result = /* generate result */
if (callback) {
callback(null, result);
} else {
return Promise.resolve(result);
}
} catch (e) {
if (callback) {
callback(e);
} else {
return Promise.reject(e);
}
}
}
And quick usage example:
connect('John')
.then(result => { /* ... */ })
.catch(error => { /* ... */ });
connect('John', (error, result) => { /* ... */ });
async function foo() {
try {
const result = await connect('John');
} catch (error) { /* ... */ }
}
Here is an example, it's similar to #hsz, but I've put the handling for the inner callback.
If your pass a callback it does this in a callback way, if not it returns a Promise instead.
If you run the snippet you can see it in action.
I've basically created a simple setTimeout function that randomly fails, to show how error handling is also done. So to see the full effect try running the snippet a few times.
function doInner(name, callback) {
setTimeout(() => {
if (Math.random() < 0.5)
callback(null, "Did " + name);
else callback(new Error("Oops in " + name));
}, 1000);
}
function doSomething(name, callback) {
if (callback) {
doInner(name, (err, result) => {
if (callback) callback(err, result);
});
} else return new Promise((resolve, reject) => {
doInner(name, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
//now lets test both ways
doSomething("test callback", (err, result) => {
if (err) console.error(err);
else console.log(result);
});
(async function () {
try {
const result = await doSomething("Promise");
console.log(result);
} catch(e) {
console.error(e);
}
}());
I am testing a conditional sequence of three async/await functions ( functionA, functionB, functionC) in a task
however when I execute it, stating that the first functionA should not pass,
1 - I don't get the expected functionA failed message
2 - how should I handle errors to stop the sequence when any function is not passing ?
thanks for feedback
console.log
functionB: functionB passed
functionC: functionC passed
task ended
ES6 js
async function functionA(duration, shouldPass) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldPass) {
resolve('functionA passed');
} else {
reject(Error('functionA failed'));
}
}, duration);
});
}
async function functionB(duration, shouldPass) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldPass) {
resolve('functionB passed');
} else {
reject(Error('functionB failed'));
}
}, duration);
});
}
async function functionC(duration, shouldPass) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldPass) {
resolve('functionC passed');
} else {
reject(Error('functionC failed'));
}
}, duration);
});
}
async function executeAsyncTask(condition1, condition2, condition3) {
let resultFunctionA = true
if (condition1) {
resultFunctionA = await functionA(3000, true)
console.log('functionA: ', resultFunctionA)
}
let resultFunctionB = true
if (resultFunctionA && condition2) {
resultFunctionB = await functionB(3000, true)
console.log('functionB: ', resultFunctionB)
}
let resultFunctionC = true
if (resultFunctionB && condition3) {
resultFunctionC = await functionC(3000, true)
console.log('functionC: ', resultFunctionC)
}
console.log('task ended')
}
// running task with condition1, condition2, condition3 parameters
executeAsyncTask(false, true, true)
1 - I don't get the expected functionA failed message
Because you are not calling functionA due to your condition1 being false.
2 - how should I handle errors to stop the sequence when any function
is not passing ?
Wrap it in a try catch block. Modified code to tailor what you are asking for.
Here's the assert from this great article: https://blog.patricktriest.com/what-is-async-await-why-should-you-care/
Here, we've wrapped the entire operation within a normal try/catch
block. This way, we can throw and catch errors from synchronous code
and asynchronous code in the exact same way. Much simpler.
async function functionA(duration, shouldPass) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldPass) {
resolve('functionA passed');
} else {
reject(Error('functionA failed'));
}
}, duration);
});
}
async function functionB(duration, shouldPass) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldPass) {
resolve('functionB passed');
} else {
reject(Error('functionB failed'));
}
}, duration);
});
}
async function functionC(duration, shouldPass) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldPass) {
resolve('functionC passed');
} else {
reject(Error('functionC failed'));
}
}, duration);
});
}
async function executeAsyncTask(condition1, condition2, condition3) {
try {
let resultFunctionA = await functionA(3000, condition1)
console.log('functionA: ', resultFunctionA)
let resultFunctionB = await functionB(3000, condition2)
console.log('functionB: ', resultFunctionB)
let resultFunctionC = await functionC(3000, condition3)
console.log('functionC: ', resultFunctionC)
console.log('task ended')
} catch (err) {
console.error(err.message)
}
}
// running task with condition1, condition2, condition3 parameters
executeAsyncTask(false, true, true)
Why are you return Promise in async function. When you write async JS will wrapped your function in Promise.
Well all your function returns Promise it means that you have to use 2 times async
I want to stop promise chain after it resolved via some conditions. Below code is might useful to understand what am I saying.
function update(id, data) {
return new Promise((resolve, reject) => {
let conn;
pool.get()
.then((db) => {
conn = db;
if(Object.keys(data).length === 0) {
return resolve({ updated: 0 });
}
else {
return generateHash(data.password);
}
})
.then((hash) => {
conn.query("UPDATE ... ", (err, queryResult) => {
if(err) {
throw err;
}
resolve({ updated: queryResult.affectedRows });
});
})
.catch((err) => { ... })
});
}
Note that pool.get() is promise wrapped API for getting connection pool from MySQL module that I made.
What I'm trying to do is updating user data. And for save server resources, I avoided to update if no data to update(Object.keys(data).length === 0).
When I tried this code, second then(updating db) is always happening even if no data to update!
I read this post, but it didn't worked. Why the promise chain wasn't stopped when I called "return resolve();"? And how to I stop it properly? I really like using Promises, but sometimes, this kind of things make me crazy. It will be very appreciate to help me this problem. Thanks!
P.S. I'm using node v6.2.2 anyway.
Why the promise chain wasn't stopped when I called "return resolve();"?
You've returned from the current then callback and fulfilled the outer promise. But that doesn't "stop" anything, then then chain still will continue by resolving with the return value of the callback.
And how to I stop it properly?
You need to put the then call inside the if to have the condition apply to it:
pool.get()
.then((db) => {
…
if (Object.keys(data).length === 0) {
…({ updated: 0 });
} else {
return generateHash(data.password)
.then((hash) => {
conn.query("UPDATE ... ", (err, queryResult) => {
…
});
})
}
})
.catch((err) => { ... })
And in any case, you should avoid the Promise constructor antipattern! You should only promisify the query method:
function query(conn, cmd) {
return new Promise((resolve, reject) => {
conn.query(cmd, (err, queryResult) => {
if (err) reject(err); // Don't throw!
else resolve(queryResult);
});
});
}
and then use that:
function update(id, data) {
return pool.get()
.then(conn => {
if (Object.keys(data).length === 0) {
conn.close(); // ???
return { updated: 0 };
} else {
return generateHash(data.password)
.then(hash => {
return query(conn, "UPDATE ... ")
}).then(queryResult => {
conn.close(); // ???
return { updated: queryResult.affectedRows };
}, err => {
…
conn.close(); // ???
});
}
});
}
Notice that it might not make sense to get a connection from the pool if you can know beforehand that no query will be made, so probably you should put the if on the top level:
function update(id, data) {
if (Object.keys(data).length === 0) {
return Promise.resolve({ updated: 0 });
} else {
return pool.get()
.then(conn => {
return generateHash(data.password)
.then(hash => {
return query(conn, "UPDATE ... ")
}).then(queryResult => {
conn.close(); // ???
return { updated: queryResult.affectedRows };
}, err => {
…
conn.close(); // ???
});
});
}
}
This would be a good situation to use an if statement:
function update(id, data) {
if (Object.keys(data).length === 0) {
return Promise.resolve({ updated: 0 });
}
let conn;
return pool.get()
.then((db) => {
conn = db;
return generateHash(data.password);
})
.then((hash) => {
return new Promise(function (resolve, reject) {
conn.query("UPDATE ... ", (err, queryResult) => {
if(err) {
reject(err);
}
resolve({ updated: queryResult.affectedRows });
});
});
})
.catch((err) => { ... })
}