Get messages from event hub to variable - javascript

I am absolutely new to Azure Event Hub.
I checked documentation and see that messages are set to console log.
async function main() {
console.log(`Running receiveEvents sample`);
const consumerClient = new EventHubConsumerClient(consumerGroup, connectionString);
const subscription = consumerClient.subscribe(
{
// The callback where you add your code to process incoming events
processEvents: async (events, context) => {
// Note: It is possible for `events` to be an empty array.
// This can happen if there were no new events to receive
// in the `maxWaitTimeInSeconds`, which is defaulted to
// 60 seconds.
// The `maxWaitTimeInSeconds` can be changed by setting
// it in the `options` passed to `subscribe()`.
for (const event of events) {
console.log(
`Received event: '${JSON.stringify(event.body)}' from partition: '${context.partitionId}' and consumer group: '${context.consumerGroup}'`
);
}
},
processError: async (err, context) => {
console.log(`Error : ${err}`);
}
},
{ startPosition: earliestEventPosition }
);
// Wait for a bit before cleaning up the sample
setTimeout(async () => {
await subscription.close();
await consumerClient.close();
console.log(`Exiting receiveEvents sample`);
}, 30 * 1000);
}
main().catch((error) => {
console.error("Error running sample:", error);
});
I would like to have this one `Received event:
${JSON.stringify(event.body)}
in variable and then operate it in thenable.
How can I do this?
Also is any possibility to filter events?
I haven't found any example.

What I understood from your question, you want a thenable (promise) async processing of the event. You can do something like below.
async function main() {
console.log(`Running receiveEvents sample`);
const consumerClient = new EventHubConsumerClient(consumerGroup, connectionString, eventHubName);
const subscription = consumerClient.subscribe(
{
// The callback where you add your code to process incoming events
processEvents: async (events, context) => {
// Note: It is possible for `events` to be an empty array.
// This can happen if there were no new events to receive
// in the `maxWaitTimeInSeconds`, which is defaulted to
// 60 seconds.
// The `maxWaitTimeInSeconds` can be changed by setting
// it in the `options` passed to `subscribe()`.
for (const event of events) {
console.log(
`Received event: '${JSON.stringify(event.body)}' from partition: '${context.partitionId}' and consumer group: '${context.consumerGroup}'`
);
processAsync(event)
.then(e => {
console.log('Done processing event');
});
}
},
processError: async (err, context) => {
console.log(`Error : ${err}`);
}
},
{ startPosition: earliestEventPosition }
);
const processAsync = async (event) => {
// do something
// you have access to the full event object
return event;
};
// Wait for a bit before cleaning up the sample
setTimeout(async () => {
await subscription.close();
await consumerClient.close();
console.log(`Exiting receiveEvents sample`);
}, 86400 * 1000);
}
main().catch((error) => {
console.error("Error running sample:", error);
});

Related

How to automate testing function with click handler that have async await inside the function using karma-jasmine?

So i'm trying to testing on my button that run the function asynchronously. this is my button logic looks like.
// Function below will run when user click the button
this._pageModule.favoriteButtonCallback = async () => {
try {
// I want to expect run after this await below is done
await this._favoriteRestaurants.PutRestaurant(this._restaurant);
console.log(
'console log in button',
await this._favoriteRestaurants.GetAllRestaurant(),
);
this._renderButton();
return Promise.resolve(
`Success add ${this._restaurant.name} to favorite!`,
);
} catch (err) {
this._renderButton();
return Promise.reject(
new Error(
`Failed add ${this._restaurant.name} to favorite! Error: ${err}`,
).message,
);
}
};
and this is my test
fit('should be able to add the restaurant to favorite', async () => {
expect((await RestaurantIdb.GetAllRestaurant()).length).toEqual(0);
// spyOn(RestaurantIdb, 'PutRestaurant');
document.body.innerHTML = `<detail-module></detail-module>
<modal-element></modal-element>`;
const pageModule = document.querySelector('detail-module');
await FavoriteButtonInitiator.init({
pageModule,
restaurant,
favoriteRestaurants: RestaurantIdb,
});
pageModule.restaurantDetail = restaurant;
await pageModule.updateComplete;
const favoriteButton = pageModule.shadowRoot
.querySelector('[aria-label="favorite this restaurant"]')
.shadowRoot.querySelector('button');
// 1. Simulate user click the button
favoriteButton.dispatchEvent(new Event('click'));
// expect(RestaurantIdb.PutRestaurant).toHaveBeenCalled();
const restaurants = await RestaurantIdb.GetAllRestaurant();
console.log('console log from test', restaurants);
expect(restaurants).toEqual([restaurant]);
});
i'm using lit-element, simply it similar with react, i have custom element <define-module> with button inside. then i give the required properties to it, then it will render.
This is my test log Test log
as you can see the console log from the test ran before the console log that i put in the button. and it is empty array.
what i want is when click event dispatched. the next line in the test wait until the asynchronous function in the button done, how do i make it possible?
What have i done:
i have tried to console log them.
i have tried to using done in jasmine, but it doesn't work since i using async/await in the test.
I have tried use spyOn, but i don't really understand how to spy indexedDb
UPDATE
So i have found what caused problem, here i have simplified my code.
/* eslint-disable */
import { openDB } from 'idb';
import { CONFIG } from '../src/scripts/globals';
const { DATABASE_NAME, DATABASE_VERSION, OBJECT_STORE_NAME } = CONFIG;
const dbPromise = openDB(DATABASE_NAME, DATABASE_VERSION, {
upgrade(database) {
database.createObjectStore(OBJECT_STORE_NAME, { keyPath: 'id' });
},
});
const RestaurantIdb = {
async GetRestaurant(id) {
return (await dbPromise).get(OBJECT_STORE_NAME, id);
},
async GetAllRestaurant() {
return (await dbPromise).getAll(OBJECT_STORE_NAME);
},
async PutRestaurant(restaurant) {
if (await this.GetRestaurant(restaurant.id)) {
return Promise.reject(
new Error('This restauant is already in your favorite!').message,
);
}
return (await dbPromise).put(OBJECT_STORE_NAME, restaurant);
},
async DeleteRestaurant(id) {
if (await this.GetRestaurant(id)) {
return (await dbPromise).delete(OBJECT_STORE_NAME, id);
}
return Promise.reject(
new Error('This restauant is not in favorite!').message,
);
},
};
describe('Testing RestaurantIdb', () => {
const removeAllRestaurant = async () => {
const restaurants = await RestaurantIdb.GetAllRestaurant();
for (const { id } of restaurants) {
await RestaurantIdb.DeleteRestaurant(id);
}
};
beforeEach(async () => {
await removeAllRestaurant();
});
afterEach(async () => {
await removeAllRestaurant();
});
it('should add restaurant', async () => {
document.body.innerHTML = `<button></button>`;
const button = document.querySelector('button');
button.addEventListener('click', async () => {
await RestaurantIdb.PutRestaurant({ id: 1 });
});
button.dispatchEvent(new Event('click'));
setTimeout(async () => {
const restaurants = await RestaurantIdb.GetAllRestaurant();
console.log('console log in test', restaurants);
expect(restaurants).toEqual([{ id: 1 }]);
}, 0);
});
});
And this is the result Test Result
I assume that IndexedDb takes times to put my restaurant data. and i still can't figure out how to fix it.
If you were using Angular, you would have access to fixture.whenStable(), and fakeAsync and tick() which wait until promises are resolved before carrying forward with the test.
In this scenario, I would try wrapping what you have in the test in a setTimeout
fit('should be able to add the restaurant to favorite', async () => {
expect((await RestaurantIdb.GetAllRestaurant()).length).toEqual(0);
// spyOn(RestaurantIdb, 'PutRestaurant');
document.body.innerHTML = `<detail-module></detail-module>
<modal-element></modal-element>`;
const pageModule = document.querySelector('detail-module');
await FavoriteButtonInitiator.init({
pageModule,
restaurant,
favoriteRestaurants: RestaurantIdb,
});
pageModule.restaurantDetail = restaurant;
await pageModule.updateComplete;
const favoriteButton = pageModule.shadowRoot
.querySelector('[aria-label="favorite this restaurant"]')
.shadowRoot.querySelector('button');
// 1. Simulate user click the button
favoriteButton.dispatchEvent(new Event('click'));
// expect(RestaurantIdb.PutRestaurant).toHaveBeenCalled();
setTimeout(() => {
const restaurants = await RestaurantIdb.GetAllRestaurant();
console.log('console log from test', restaurants);
expect(restaurants).toEqual([restaurant]);
}, 0);
});
The things in the setTimeout should hopefully happen after the asynchronous task of the button click since promises are microtasks and setTimeout is a macrotask and microtasks have higher priority than macrotasks.

Custom status change events in Javascript

I have an asynchronous function that performs various await tasks. I am trying to inform my UI in React when the status of the function changes or when one of the tasks is completed.
const foo = async () => {
// trigger on load event
await task1();
// trigger task1 done event
await task2();
// trigger task2 done event
await task3();
// trigger on done event
}
I also want to be able to specify callbacks for each event, like so:
const bar = foo();
foo.on_load(() => {
// some code goes here
});
foo.on_done(() => {
// some code goes here
});
Another alternative would be something like this:
const bar = foo();
foo.on('status_change', status => {
// read the status here and do something depending on the status
})
I have been reading about custom events in JS but not sure how to use them for this. Or maybe there's another way to do this in React.
Any ideas would be helpful. Thanks!
EDIT
var uploadTask = storageRef.child('images/rivers.jpg').put(file);
// Register three observers:
// 1. 'state_changed' observer, called any time the state changes
// 2. Error observer, called on failure
// 3. Completion observer, called on successful completion
uploadTask.on('state_changed', function(snapshot){
// Observe state change events such as progress, pause, and resume
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
switch (snapshot.state) {
case firebase.storage.TaskState.PAUSED: // or 'paused'
console.log('Upload is paused');
break;
case firebase.storage.TaskState.RUNNING: // or 'running'
console.log('Upload is running');
break;
}
}, function(error) {
// Handle unsuccessful uploads
}, function() {
// Handle successful uploads on complete
// For instance, get the download URL: https://firebasestorage.googleapis.com/...
uploadTask.snapshot.ref.getDownloadURL().then(function(downloadURL) {
console.log('File available at', downloadURL);
});
});
I was trying to achieve something like the above code, taken from the firebase documentation on uploading files
This is where I've gotten so far:
class Task {
constructor() {
this.first = null;
this.second = null;
}
on(keyword, callback) {
switch (keyword) {
case "first":
this.first = callback;
break;
case "second":
this.second = callback;
break;
default:
// throw new error
break;
}
}
}
const timeout = async time => {
return new Promise(resolve => setTimeout(resolve, time));
};
const foo = () => {
const task = new Task();
timeout(2000).then(async () => {
task.first && task.first();
await timeout(2000);
task.second && task.second();
});
console.log("returning");
return task;
};
const taskObject = foo();
taskObject.on("first", () => console.log("executing first callback"));
taskObject.on("second", () => console.log("executing second callback"));
Is there a better way to do this - without having the nested thens? Which approach would be better and when? EDIT - removed nested then clauses and replaced with then and await
PS: for my requirements, having callbacks would be sufficient. This is just so I can understand the concept better. Thanks!
I'm going to assume there's a reason for you not simply calling some named method after each async step has complete, i.e., you want to be able to plug in different handlers for each event. Here is one way to go about it - whether or not it's the best is hard to tell from the little context provided:
const foo = async (handlers) => {
handlers.onLoad && handlers.onLoad();
await task1();
handlers.onTask1Complete && handlers.onTask1Complete();
await task2();
handlers.onTask2Complete && handlers.onTask2Complete();
}
const myHandlers = {
onLoad: () => {
// do stuff
},
onTask1Complete: () => {
// do other stuff
},
onTask2Complete: () => {
// etc
}
};
foo(myHandlers);
Note that it lets you specify only the handlers you need. A more flexible approach would be to a publish-subscribe model, where a subscribe method pushes a function to an array of handlers, all of which are called when the event occurs.
The best option would be to make use of promises, which means every time a promise is resolved, you will get notified and then cascading promise will get executed.
an example below of chaining promises
var function3 = function(resolve, reject)
{
try
{
//do some thing
console.log('function3 called');
resolve('function3 success');
}
catch(err)
{
reject(err);
}
}
var function2 = function(resolve, reject)
{
try
{
//do some thing
console.log('function2 called');
resolve('function2 success');
//return new Promise(function3);
}
catch(err)
{
reject(err);
}
}
var function1 = function(resolve, reject)
{
try
{
//do some thing
console.log('function1 called');
resolve('function1 success');
}
catch(err)
{
reject(err);
}
}
var promise = new Promise(function1);
promise
.then(function(response){
console.log(response);
return new Promise(function2);
}, function(error)
{
console.log(error);
})
.then(function(response)
{
console.log(response);
return new Promise(function3);
},
function(err)
{
console.log(error);
})
.then(function(response)
{
console.log(response);
},
function(err)
{
console.log(error);
})
//output
"function1 called"
"function1 success"
"function2 called"
"function2 success"
"function3 called"
"function3 success"

socket.io and async events

I'm using socket.io and mongoose in my express server.
My socket is listening for events using the following code:
socket.on('do something', async () => {
try {
await doA();
doX();
await doB();
doY();
await doC();
} catch (error) {
console.log(error);
}
});
doA, doB and doC are async operations that writes on database using mongoose, but in general they can be any method returning a promise.
I want that 'do something' runs synchronously.
If the event queue processes more events at the same time I have consistency problems in my mongodb.
In other words if the server receives two 'do something' events, I want that the second event received is processed only when the first event is fully processed (after the await doC). Unfortunately the 'do something' callback is async.
How to handle this?
It's possible to implement a queue by adding the functions you want to run to an array, and then running them one by one. I've created an example below.
let queue = [];
let running = false;
const delay = (t, v) => {
return new Promise((resolve) => {
setTimeout(resolve.bind(null, "Returned value from Promise"), t)
});
}
const onSocketEvent = async () => {
console.log("Got event");
if (!running) {
console.log("Nothing in queue, fire right away");
return doStuff();
}
// There's something in the queue, so add it to it
console.log("Queuing item")
queue.push(doStuff);
}
const doStuff = async () => {
running = true;
const promiseResult = await delay(2000);
console.log(promiseResult);
if (queue.length > 0) {
console.log("There's more in the queue, run the next one now")
queue.shift()();
} else {
console.log("Queue empty!")
running = false;
}
}
onSocketEvent();
setTimeout(() => onSocketEvent(), 1000);
setTimeout(() => onSocketEvent(), 1500);
setTimeout(() => onSocketEvent(), 2000);
setTimeout(() => onSocketEvent(), 2500);
I would suggest adding a delay between each await. This will prevent deadlocks from occurring and fix your issue. For such things, I would suggest using the Caolan's async library.
Task delay example:
setTimeout(function() { your_function(); }, 5000); // 5 seconds
If your function has no parameters and no explicit receiver, you can call directly setTimeout(func, 5000)
Useful jQuery timers plugin

How to know when asynchronous forEach is finished

I want to call a callback when both forEach are done. I want to know when all of them are done processing asynchronously and call a callback. console.log("Done") seems to finish before the two forEach
const getDates = () => {
const ref = db.ref("reminders");
const dateTime = new Date();
const currentDate = dateFormat(dateTime, "yyyy-mm-dd");
ref
.orderByChild('date')
.endAt(currentDate)
.once('value', (reminderDates) => {
reminderDates.forEach((singleDate) => {
// iterate over reminder dates
singleDate.forEach( (notificationValues) => {
// iterate over notification codes
if (!notificationValues.key.includes('date')) {
processNotifications(notificationValues, () => {
console.log(`Sent notification reminder at ${notificationValues.key}`);
});
}
});
});
}).catch( (error) => {
console.log(error);
});
console.log("Done")
};
Output
Done
AB000001_AB0977 { subtitle: 'Time to start thinking about making a payment',
title: 'School Semester 1, 2019 School Fees',
userId: 'kXnfHPyxfpeLQ1aCjvl8Pu09sssslou1' } d-ktpdo45SQ:APA91bF5rJtaHvtNUE42GDssssXoOAP_r7omRmsIs44WKnABsMC8lintdoDBzUYrZ5lutEKECwuaOOIQtdZkKW5Apt4A0ssssyZwdl_epdI2dYHkhk0h-Yns6jzlMbIltSHasA40YL725sssL9TmyCd
Sent notification reminder at AB000001_AB0977
From the docs:
once
once(eventType: EventType, successCallback?: function, failureCallbackOrContext?: Object | null, context?: Object | null): Promise<DataSnapshot>
once returns a Promise which means it is asynchronous, therefore the console.log("Done") will be printed before your forEach(). You cannot know when the asynchronous operation will be finished.
Therefore, the best way to solve it is to add console.log("Done") inside the forEach():
.once('value', (reminderDates) => {
reminderDates.forEach((singleDate) => {
// iterate over reminder dates
singleDate.forEach( (notificationValues) => {
// iterate over notification codes
if (!notificationValues.key.includes('date')) {
processNotifications(notificationValues, () => {
console.log(`Sent notification reminder at ${notificationValues.key}`);
console.log("Done");
});
}
});
});
I don't realy use firebase but if you want to wait for multiple asynchronus operations you can use Promise.all
You just have to push inside an array all your async operation. Once it's finish juste write something like :
Promise.all(yourArrayOfPromise)
.then(() => {
console.log('success');
})
.catch(err => {
console.log(err);
})

Redis Javascript Async Function

I have an array of Id' and i need to get the details for each of them.
i currently have this.
const redis = require('redis');
const redisClient = redis.createClient(process.env.REDIS_PORT, process.env.REDIS_HOST);
const arrayList = [
{ id: 3444 },
{ id: 3555 },
{ id: 543666 },
{ id: 12333 },
];
async function getDetails(element) {
await redisClient.hgetall(element.id, (err, user) => {
if (err) {
console.log('Something went wrong');
// Handle Error
return err;
}
console.log('Done for User');
return user;
});
}
arrayList.forEach((element) => {
console.log('element');
await getDetails(element).then((res) => {
// Do Something with response for each element
});
});
This is the response i get right now. its not async. What am i doing wrong please.
element
element
element
element
Done for User
Done for User
Done for User
Done for User
So how things go on in async/await is, you create an async function and inside that function you await for other operations to finish. You call that async function without await OR you wrap it(func call) inside another async function.
arrayList.forEach((element) => {
console.log('element');
let returnedPromise= getDetails(element);
console.log("Promise after getDetails function", returnedPromise);
});
This code change should resolve the error.
Array.forEach() does not wait for promises to execute before moving to the next item.
You could instead use a for-loop in an async function, like so:
async function main() {
for (const element of arrayList) {
const response = await getDetails(element);
// do something with reponse for each element
}
}
main()
.then(() => /* on success */)
.catch((err) => /* on error */);

Categories

Resources