I am trying to promisify (or leverage async await feature) the callback in the function getId, using new Promise(resolve => ..., but because of the usage of the higher order function getAddresses, I am a bit stumped. I am not great at functional programming. Any suggestions on how do I promisify this one?
const {queryRecord, getData} = require(“#xyzLib”);
const getId = (callback) => {
getData(“attr1”,”attr2”,getAddresses(callback));
}
const getAddresses = (callback) => (result) => {
if (!result.success) {
callback(new Error(‘Exception details’))
} else {
queryRecord(objectName, (result) => {
callback(null, result.name);
});
}
}
// invoking call
getId(async (error, zip, state) => {
if (error) {
console.log(error.message)
} else {
await fetch(encodeURI(settingsUrl), {
method: 'GET',
});
....
Since getId() accepts a callback as the last argument and that callback uses the nodejs calling convention, you can just directly use util.promisify() on getId like this:
const { promisify } = require('util');
const getIdPromise = promisify(getId);
getIdPromise().then(result => {
console.log(result);
let fetchResult = await fetch(...);
...
}).catch(err => {
console.log(err);
});
Or, if you're already inside an async function body:
try {
const result = await getIdPromise();
console.log(result);
const fetchResult = await fetch(...);
...
} catch (err) {
console.log(err);
}
You should promisify on the lowest level available, i.e. the imported functions from the library. Then you don't even need that currying. With
const { promisify } = require('util');
const xyz = require(“#xyzLib”);
const queryRecord = promisify(xyz.queryRecord);
const getData = promisify(xyz.getData);
you can write simply
async function getId() {
return getAddresses(await getData("attr1", "attr2"));
}
async function getAddresses(data) {
if (!data.success) throw new Error("Exception details");
const result = await queryRecord(objectName);
return result.name;
}
// invoking call
try {
const zip = getId();
await fetch(encodeURI(settingsUrl), {
method: 'GET',
});
} catch(error) {
console.log(error.message);
}
Related
In my project I have a function for downloading the files. When click the button the function onDownload will be called:
import {useOnDownload} from "../../use/useOnDownload"
setup() {
...
const loading = ref(null)
onDownload = (id) => {
loading.value = id
await useOnDownload(id)
loading.value = null
}
return {loading, onDownload}
}
I refactored the code for api in a file useOnDownload.js call because the same code is used in another components as well.
export async function useOnDownload(id) {
// make api call to server with axios
}
What I did wrong? I need to wait for the function useOnDownload ... in order the loader to work.
Here is how to make async composable functions with async await syntax
export default function useOnDownload() {
const isLoading = ref(true);
const onDownload = async () => {
isLoading.value = true;
try {
const { data } = await axios.post('/api/download', {id: id},
{responseType: 'blob'})
// handle the response
} catch (error) {
console.log(error);
} finally {
isLoading.value = false;
}
};
// invoke the function
onDownload();
return { // return your reactive data here };
}
import useOnDownload from "../../use/useOnDownload"
// no await in setup script or function
const { reactiveDataReturned } = useOnDownload();
Read more here
onDownload must be async in order to use await within it
I managed to solved another way without async and await...
I passed the reference object loader to the function parameter (as optional) and handle from there...
export function useOnDownload(id, loader) {
if(loader !== undefined) {
loader.value = id
}
axios.post('/api/download', {id: id}, {
responseType: 'blob'
}).then(response => {
// handle the response
...
if(loader !== undefined) {
loader.value = null
}
}).catch(error => {
// handle the error
...
if(loader !== undefined) {
loader.value = null
}
})
}
You are using the await keyword in your onDownlaod function, but the function is not asynchronous. Here's how you should update it.
// next line important
onDownload = async(id) => {
loading.value = id
await useOnDownload(id)
loading.value = null
}
The Problem is with the uplines.push.
I always get an empty uplines array so the last part of the code doesn't run. The promises resolve later and I get the correct data. May I know how to go about doing it the correct way?
const getAllUplines = async () => {
uplines = [];
const findUser = async (userFid) => {
const userDoc = await firestore.collection("users").doc(userFid).get();
if (userDoc.exists) {
const user = { ...userDoc.data(), id: userDoc.id };
console.log(user);
uplines.push(user);
if (user.immediateUplineFid) {
findUser(user.immediateUplineFid); //self looping
}
} else {
console.log("No User Found");
return null;
}
};
sale.rens.forEach(async (ren) => {
findUser(ren.userFid);
});
console.log(uplines);
return uplines;
};
let uplines = await getAllUplines();
console.log(uplines);
uplines = uplines.filter(
(v, i) => uplines.findIndex((index) => index === v) === i
); //remove duplicates
uplines.forEach((user) => {
if (user.chatId) {
sendTelegramMessage(user.chatId, saleToDisplay, currentUser.displayName);
console.log("Telegram Message Sent to " + user.displayName);
} else {
console.log(user.displayName + " has no chatId");
}
});
There are a few things that you have missed out while implementing the async call, which are explained in the inline comments in the code snippet.
A short explanation for what happened in your code is that in the line sale.rens.forEach you are passing an async function in the argument, which does not make any difference to the function forEach, it will execute it without waiting for it to complete.
Therefore in my answer I am using Promise.all to wait for all the async function calls to complete before returning the result.
// This is wrapped in an immediately executed async function because await in root is not supported here
(async () => {
const mockGetData = () => new Promise(resolve => setTimeout(resolve, 1000));
const sale = {
rens: [
{ userFid: 1 },
{ userFid: 2 },
{ userFid: 3 }
]
};
const getAllUplines = async () => {
const uplines = [];
const findUser = async (userFid) => {
// Simulating an async function call
const userDoc = await mockGetData();
console.log("User data received");
uplines.push(`User ${userFid}`);
};
const promises = [];
sale.rens.forEach(ren => { // This function in foreach does not have to be declared as async
// The function findUser is an async function, which returns a promise, so we have to keep track of all the promises returned to be used later
promises.push(findUser(ren.userFid));
});
await Promise.all(promises);
return uplines;
};
let uplines = await getAllUplines();
console.log(uplines);
})();
In order to get the results of getAllUplines() properly, you need to add await to all async functions called in getAllUplines().
const getAllUplines = async () => {
uplines = [];
const findUser = async (userFid) => {
const userDoc = await firestore.collection("users").doc(userFid).get();
if (userDoc.exists) {
const user = { ...userDoc.data(), id: userDoc.id };
console.log(user);
uplines.push(user);
if (user.immediateUplineFid) {
await findUser(user.immediateUplineFid); //self looping
}
} else {
console.log("No User Found");
return null;
}
};
sale.rens.forEach(async (ren) => {
await findUser(ren.userFid);
});
console.log(uplines);
return uplines;
};
Within my express.js app I've 2 modules:
the first is an initializer module, which I call at the beginning of the second; the main module.:
mainModule.js
const result = require('initilalizerModule')
...
initilalizerModule.js:
const soap = require('soap');
const path = require('path');
//passed en params for DAO
const endpoint = 'https://myurl.com';
const url = path.resolve(__dirname, 'mycontract.wsdl');
const soapOptions = {
forceSoap12Headers: true,
connection: 'keep-alive',
disableCache: false
};
function initialize() {
console.log("test")
return new Promise((resolve,reject) => {
soap.createClient(url, soapOptions, function (err, RESULT) {
if (err) {
reject('err');
}
else {
client.setEndpoint(endpoint);
resolve(RESULT);
}
});
})
}
module.exports = {
myResult : ....
}
I have this asynchronous initialize() method which brings a RESULT
My purpose is how to export this RESULT object from my initilizerModule to be used after that in my mainModule?
You have to understand async programming. require is synchronous and cached by default. Anything that you want to perform after some task async. You have to use the callback. Here is a basic example.
// main.js
const {init} = require("./provider")
init((data) => {
console.log(data) // somedata
})
//provider.js
const someDelay = () => new Promise(r => {
setTimeout(() => r("somedata"), 1000)
})
exports.init = (cb) => {
someDelay().then(cb)
}
If you are using the lastest node.js. You can use async/await.
async/await version
// main.js
const {init} = require("./provider")
async function start() {
const data = await init()
console.log(data) // somedata
}
start()
// provider.js
const someDelay = () => new Promise(r => {
setTimeout(() => r("somedata"), 1000)
})
exports.init = async () => {
return await someDelay()
}
Hope this will answer your question! Cheers.
On a quick look you can define your initialize() function as a variable, which you can export, e.g. const myResult = function initialize() {...
and then use
module.exports = { myResult }
I'm trying deal with a library that using async functions and am a little lost. I want to call a function that returns a string but am getting tripped up. Here's what I have so far. The ZeroEx library functions all seem to use async /await so my understanding is that I can only call them from another async method. But won't this just cause a chain reaction meaning every method needs to be async? Or am I missing something?
function main() {
var broker = zmq.socket('router');
broker.bindSync('tcp://*:5671');
broker.on('message', function () {
var args = Array.apply(null, arguments)
, identity = args[0]
, message = args[1].toString('utf8');
if(message === 'TopOfBook') {
broker.send([identity, '', getTopOfBook()]);
}
//broker.send([identity, '', 'TEST']);
//console.log('test sent');
})
}
async function getTopOfBook() {
var result: string = 'test getTopOfBook';
const EXCHANGE_ADDRESS = await zeroEx.exchange.getContractAddress();
const wethTokenInfo = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync('WETH');
const zrxTokenInfo = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync('ZRX');
if (wethTokenInfo === undefined || zrxTokenInfo === undefined) {
throw new Error('could not find token info');
}
const WETH_ADDRESS = wethTokenInfo.address;
const ZRX_ADDRESS = zrxTokenInfo.address;
return result;
}
main();
The function getTopOfBook() isn't returning anything back to the main() function so the result is never being sent by the broker. The commented out broker.send() with 'TEST' is working fine however. Thanks for looking.
EDIT:
I tried to make the main method async so I could use await but it's giving an error that I can only use await in an async function. Could the broker.on() call be causing this?
const main = async () => {
try{
var broker = zmq.socket('router');
broker.bindSync('tcp://*:5671');
broker.on('message', function () {
var args = Array.apply(null, arguments)
, identity = args[0]
, message = args[1].toString('utf8');
console.log(message);
if(message === 'TopOfBook') {
>> var test = await getTopOfBook();
console.log('in top of book test');
broker.send([identity, '', test]);
}
//broker.send([identity, '', 'TEST']);
//console.log('test sent');
})
} catch (err) {
console.log(err);
}
}
EDIT 2:
My current working code, thanks everyone that had advice/solutions! I obviously have to fill out the getTopOfBook() function to return an actual result still. If you have more recommendations send them my way. I'm trying to build out a backend that will get data from a geth rpc and send it to a C# GUI front end.
var main = function() {
try{
var broker = zmq.socket('router');
broker.bindSync('tcp://*:5672');
broker.on('message', function () {
var args = Array.apply(null, arguments)
, identity = args[0]
, message = args[1].toString('utf8');
if(message === 'TopOfBook') {
getTopOfBook().then((result) => {
broker.send([identity, '', result])
});
}
})
} catch (err) {
console.log(err);
}
}
async function getTopOfBook() {
var result: string = 'test getTopOfBook';
const EXCHANGE_ADDRESS = await zeroEx.exchange.getContractAddress();
const wethTokenInfo = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync('WETH');
const zrxTokenInfo = await zeroEx.tokenRegistry.getTokenBySymbolIfExistsAsync('ZRX');
if (wethTokenInfo === undefined || zrxTokenInfo === undefined) {
throw new Error('could not find token info');
}
const WETH_ADDRESS = wethTokenInfo.address;
const ZRX_ADDRESS = zrxTokenInfo.address;
return result;
}
main();
The callback function needs to be async
broker.on('message', async function () {
var args = Array.apply(null, arguments)
, identity = args[0]
, message = args[1].toString('utf8');
if(message === 'TopOfBook') {
var test = await getTopOfBook();
broker.send([identity, '', test]);
}
//broker.send([identity, '', 'TEST']);
//console.log('test sent');
})
You are missing the point that async functions are just a way to write the code. Every async function what actually does is generate a promise. That is why you can await any promise and you can interact with any library that uses async without the need of using async functions yourself.
When you call an async function it should return a promise (which happens automatically on async function). A promise is nothing but an object with a then method. Such method accepts a callback, where you can handle the rest of the logic.
function main() {
var broker = zmq.socket('router');
broker.bindSync('tcp://*:5671');
broker.on('message', function () {
var args = Array.apply(null, arguments)
, identity = args[0]
, message = args[1].toString('utf8');
if(message === 'TopOfBook') {
return getTopOfBook().then( result =>
broker.send([identity, '', result])
) // If the broker also returns a promise, you can continue the flow here
.then(()=> console.log('test sent'))
}
})
}
Personally I don't like async await at all because the involve too much magic and make people to forget about the actual nature of promises and asynchronous code.
Also when dealing with promises you should always remember to return any promise you could call/generate so outer code can continue the chain and handle error messages.
Your function getTopOfBook returns a Promise, so you need to use the function then
Call that function as follow:
getTopOfBook().then((result) => {
console.log("Result:" + result);
});
Look at this code snippet
let sleep = (fn) => {
setTimeout(fn, 1000);
};
let getContractAddress = function(cb) {
return new Promise((r) => sleep(() => {
r('getContractAddress')
}));
};
let getTokenBySymbolIfExistsAsync = function(str) {
return new Promise((r) => sleep(() => {
r({
address: 'getTokenBySymbolIfExistsAsync: ' + str
})
}));
};
let WETH_ADDRESS = '';
let ZRX_ADDRESS = '';
let EXCHANGE_ADDRESS = '';
async function getTopOfBook() {
var result = 'test getTopOfBook';
const EXCHANGE_ADDRESS = await getContractAddress();
const wethTokenInfo = await getTokenBySymbolIfExistsAsync('WETH');
const zrxTokenInfo = await getTokenBySymbolIfExistsAsync('ZRX');
if (wethTokenInfo === undefined || zrxTokenInfo === undefined) {
return Promise.reject(new Error('could not find token info'));
}
const WETH_ADDRESS = wethTokenInfo.address;
const ZRX_ADDRESS = zrxTokenInfo.address;
console.log(WETH_ADDRESS);
console.log(ZRX_ADDRESS);
console.log(EXCHANGE_ADDRESS);
return result;
}
var main = function() {
console.log('Waiting response...');
getTopOfBook().then((result) => {
console.log("Result:" + result);
console.log('DONE!');
}).catch((error) => {
console.log(error);
});
};
main();
.as-console-wrapper {
max-height: 100% !important
}
If you want to throw an error use the function Promise.reject()
Along with that call, you need either to pass the reject function or call the catch function.
In this example, we're passing the reject function:
(error) => {
console.log(error);
}
If you don't pass the reject function, you need to call the catch function in order to handle the thrown error.
let sleep = (fn) => {
setTimeout(fn, 1000);
};
let getContractAddress = function(cb) {
return new Promise((r) => sleep(() => {
r('getContractAddress')
}));
};
let getTokenBySymbolIfExistsAsync = function(str) {
return new Promise((r) => sleep(() => {
r()
}));
};
let WETH_ADDRESS = '';
let ZRX_ADDRESS = '';
let EXCHANGE_ADDRESS = '';
async function getTopOfBook() {
var result = 'test getTopOfBook';
const EXCHANGE_ADDRESS = await getContractAddress();
const wethTokenInfo = await getTokenBySymbolIfExistsAsync('WETH');
const zrxTokenInfo = await getTokenBySymbolIfExistsAsync('ZRX');
if (wethTokenInfo === undefined || zrxTokenInfo === undefined) {
return Promise.reject('Could not find token info');
}
const WETH_ADDRESS = wethTokenInfo.address;
const ZRX_ADDRESS = zrxTokenInfo.address;
console.log(WETH_ADDRESS);
console.log(ZRX_ADDRESS);
console.log(EXCHANGE_ADDRESS);
return result;
}
var main = function() {
console.log('Waiting response...');
getTopOfBook().then((result) => {
console.log("Result:" + result);
console.log('DONE!');
}, (error) => {
console.log(error);
}).catch((error) => {
console.log(error); // This line will be called if reject function is missing.
});
};
main();
.as-console-wrapper {
max-height: 100% !important
}
Resource
Promise.prototype.then()
Promise.reject()
Async function
When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.
I have being using Promises for a long time, and they are always the "thing" that I use to control the workflow of my programs. Example:
Promise
.resolve(obj)
.then(doSomething1)
.then(doSomething2)
.catch(handleError)
And now, I would like to change to a try-catch style, but I don't know exactly what would be the right approach.
V1:
try {
var body = await Promise
.resolve(obj)
.then(doSomething1)
.then(doSomething2)
} catch (error) {
callback(error)
}
callback(null, {
statusCode: 200,
body
})
V2:
try {
var body = await Promise
.resolve(obj)
.then(doSomething1)
.then(doSomething2)
.then(body => {
callback(null, {
statusCode: 200,
body
})
})
} catch (error) {
callback(error)
}
What would be the right approach?
You don't have to use a callback function in order to switch to async/await. An async function is just a Promise-returning function, and await is there as a convenience. So the equivalent of your original function is simply:
async function fn() {
try {
const obj = ...;
const result1 = await doSomething1(obj);
const result2 = await doSomething2(result1);
return result2;
} catch (err) {
return handleError(err);
}
}
If you do want that callback:
async function fn(callback) {
try {
const obj = ...;
const result1 = await doSomething1(obj);
const result2 = await doSomething2(result1);
callback(null, result2);
} catch (err) {
callback(err);
}
}