RxJS Observable with asynchronous subscriber function - javascript

I'm trying to do something that feels like it should be straightforward, but is proving surprisingly difficult.
I have a function to subscribe to a RabbitMQ queue. Concretely, this is the Channel.consume function here: http://www.squaremobius.net/amqp.node/channel_api.html#channel_consume
It returns a promise which is resolved with a subscription id - which is needed to unsubscribe later - and also has a callback argument to invoke when messages are pulled off the queue.
When I want to unsubscribe from the queue, I'd need to cancel the consumer using the Channel.cancel function here: http://www.squaremobius.net/amqp.node/channel_api.html#channel_cancel. This takes the previously returned subscription id.
I want to wrap all of this stuff in an Observable that subscribes to the queue when the observable is subscribed to, and cancels the subscription when the observable is unsubscribed from. However, this is proving somewhat hard due to the 'double-asynchronous' nature of the calls (I mean to say that they have both a callback AND return a promise).
Ideally, the code I'd like to be able to write is:
return new Rx.Observable(async (subscriber) => {
var consumeResult = await channel.consume(queueName, (message) => subscriber.next(message));
return async () => {
await channel.cancel(consumeResult.consumerTag);
};
});
However, this isn't possible as this constructor doesn't support async subscriber functions or teardown logic.
I've not been able to figure this one out. Am I missing something here? Why is this so hard?
Cheers,
Alex

The created observable does not need to wait for the channel.consume promise to resolve, as the observer (it's an observer that's passed, not a subscriber) is only called from within the function you provide.
However, the unsubscribe function that you return will have to wait for that promise to resolve. And it can do that internally, like this:
return new Rx.Observable((observer) => {
var consumeResult = channel.consume(queueName, (message) => observer.next(message));
return () => {
consumeResult.then(() => channel.cancel(consumeResult.consumerTag));
};
});

Related

How to return a promise resolved from a callback

I have an issue with my promise logic (still pretty new to JS), and I cannot find what is wrong.
Looking at how hard it is for me to do what I want, I can only guess that's it's not the correct way to do it. I did found another way to fix it, but I am still interested if there is a way to do what I want or not.
Here are some details:
I have a MessageQueue.sendMessage() which adds the message I give to the PendingMessageCache. When it can, the MessageQueue takes the cache and sends the Message to the server.
Sometimes I need to do some stuff after the message is sent. So I added a callback, saved and invoked by the MessageQueue when it sends the message. It's working great. The call looks basically like this:
await MessageQueue.sendMessage(myMsg, () => {markMessageAsSent()});
For one new type of message, I need to wait for the message being effectively sent because the app is completely cleaned after it.
As the sendMessage just adds the message to the cache and returns, the callback is later called but in this case it's too late, the app was restarted.
The message is basically a lastMessage sent to the server to say that this user removed the specific application instance.
So I think that what I want would be something like this:
deleteAccount = async () => {
const aMagicalCallback;
// this call adds the message to the cache,
// and ask the MessageQueue to call aMagicalCallback when that lastMsg is sent
await MessageQueue.sendLastMessage(lastMsg, <aMagicalCallback>);
// return the callback which would be a promise.
// this promise is resolved when it is resolved
// when the MessageQueue invokes the callback <aMagicalCallback>
return <aMagicalCallback>;
}
what I would like this to do is basically
// wait for the lastMessageBeingEffectively sent
await deleteAccount();
app.eraseEverything();
app.restart();
Let me know if that is not clear at all.
And thanks
You should return a Promise instead of using async. So you can choose when to resolve the promise, even in a nested callback function:
deleteAccount = () => new Promise(resolve => {
MessageQueue.sendLastMessage(lastMsg, () => {
// do your thing
resolve();
});
});
Then you can await it just like a regular async function
// this awaits until the resolve() above has been called
await deleteAccount();
app.eraseEverything();
app.restart();
As I understand it, you're already adding a callback function to every message. Instead of thinking of a separate callback to be injected after the promise is created, you could have the original callback check for the size of the message queue and execute
app.eraseEverything();
app.restart();
when it's empty.

modify promises creation and fetching to detailed profiling of async nodejs application

To profile my app more detailed I need to know which promises not awaited but... js not provide such ability.
In python every time when result of async function is not awaited it prints warring
But how to obtain such result in js?
I concluded to do so I have to know few things:
how to override promise construction (to add state field which will show is promise awaited and to set timeout to check state)
what triggered when object awaited (to change state)
I figured out via Proxy that when awaiting, object's then property is called:
async function test(obj) {
await obj
}
test(new Proxy(new Object(), {
get(obj, name) {
console.log("get " + name)
}
})
But actually, as turned out, it happens only with non-promise objects
So when I tried to do this:
Promise.prototype.then = function() {
console.log("called then")
}
test(Promise.resolve({}))
I got nothing in output.
Same things with Promise.prototype.constructor and Promise.resolve:
let getOne = async () => 1
getOne()
getOne neither call Promise constructor nor Promise.resolve.
So what I'm doing wrong ?
How actually async/await creates promises and fetch value from them under the hood, and how can it be overridden ?
I figured out via Proxy that when awaiting, object's then property is called
Indeed, that is correct.
when I tried to do [...] test(Promise.resolve({})) I got nothing in output.
That is because in this code -- contrary to your earlier test with await -- you do not execute an await or a then call here. You just construct a promise, and your proxy object is not trapping the creation of a promise, only the execution of the then method.
This brings us back to your first bullet point:
how to override promise construction?
You cannot.
We have access to the Promise constructor, but there is no way you can trap its execution. If you would make a proxy for it -- with a construct handler -- it would not be called, as the JavaScript engine will still refer to the original Promise constructor when -- for instance -- an async function is executed. So you are without hope here.
I don't believe overriding is something you want. Usually async is telling node that this function is asynchronious and if you have something in the event loop that is "long" procedure such as querying another API or reading IO then those functions will yield promises. You can also try using the following:
return new Promise((resolve, reject) => {... do stuff and call resolve() or reject() based on the expected output})
The returned value will be a promise.
To handle the promise you will need to await it.
To get promise from
let getOne = async () => 1 you will need to make it
let getOne = async () => return new Promise((resolve, reject) => return resolve(1))
To obtain the value later you will need to call await getOne()

Wait till the subscribe finishes then move on to next part of the code [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
Have a subscription thats being called after everything else gets set but i wanna wait till the subscription finshes. T
Tried using async await but that didnt work with me. No sure if I was doing it incorrectly
public getGlobeConfig() {
this.sourceKey = 'test';query = 'query'
// wait till this call finishes than hit the
console.log('htting')
this.service
.searchQuery(query)
.subscribe(response => {
this.sources = response.hits.hits.map(hit =>
hit._source);
if (this.sources.length > 0) {
this.availableMetadata = Object.keys(
this.sources[0].metadata[this.sourceKey]
);
}
});
console.log('hitting')
return this.sources
}
This.sources is reaching as undefined because this.sources is being set in the subscriptioon
The short answer is you cannot cause the code after the subscribe to wait. Having said that, by taking a step back and looking at your code, you should not subscribe within the getGlobeConfig method. What you probably should do is use the map operator within the getGlobeConfig method and let the consumer of the getGlobeConfig method subscribe:
public getGlobeConfig() {
// ...
return this.service.searchQuery(query).pipe(
map(response => {
// Transform the response to what you want
// the consumer of this method to receive
})
);
}
Consumer:
getGlobeConfig().subscribe(sources => /* ... */)
One very common pitfall I am seeing from new RxJs developers is that they try to subscribe to Observables in a service and then want to return the data to the components. In most cases you do not subscribe within the services. Let the services operate on the data within RxJs operators and have the services return the transformed Observables. The end consumer (usually components) will then subscribe to the Observables returned by the services.
Your problem is that you cannot return a synchronous value that is generated in an asynchronous call. The best you can do is return a promise (or other async object). This is what async await is designed to accomplish: it adds keywords that make it easier to wait for promises to finish but in the end you are still working with promises, and async functions always return promises.
Here's some simple examples:
function doSomethingWithPromise() {
return somethingThatReturnsAPromise()
.then((result) => result.calculateSomethingFromResult()) // `then` returns a promise with a result of the callback
}
Transformed to an async call:
async function doSomethingWithAsync() {
// because this is an async function, it returns a promise here, before it finishes waiting
let result = await somethingThatReturnsAPromise()
return result.calculateSomethingFromResult() // at this point, after waiting, the function fulfills the promise with this return value
}
Those two examples are equivalent.
(This is a general example, and if you are using a library that uses streams or events instead of promises, for example, things may be different)

Resolve await when message arrives

I have some websocket code in JS. I have a message-handling loop like this:
socket.addEventListener('message', function (event) {
payload = JSON.parse(event.data)
method = payload.method
// Dispatch messages
if (method == 'cmd1') {
handle_cmd1(payload); // trigger event/semaphore here to wait up waiter
}
else if (method == 'cmd2') { ... }
});
And elsewhere, I have a button callback like this:
$('#my-button').change(function() {
handle_button();
});
async function handle_button() {
send_msg('msg1', 'hi');
// wait for server to reply with cmd1
cmd1_data = await something(); // what?
alert(`cmd1 data: $(cmd1_data)`);
}
The idea is that the button sends 'msg1' and the server is supposed to reply with 'cmd1' and some info. I want to wait for that reply and then do some more stuff.
So my question is how to interlock these? In C++ I'd use a semaphore. I'd rather not spin-loop; is there something in Javascript/JQuery I can use to trigger and then wait for a user-defined event like this? I'm sort of new to JS, and very new to JS async/await.
EDIT: I've made a simple jsfiddle to show what I'm after.
http://jsfiddle.net/xpvt214o/484700/
Now that I understand how promises in Javascript work, here's a working example of a promise that can get woken up from anywhere by calling a function:
wakeup = null;
// returns a promise that will be resolved by calling wakeup()
// (could be a list of these or whatever, this is just a simple demo)
function wakeable() {
return new Promise( (resolve) => {
wakeup = () => { resolve(true); }
});
}
// demo of waiting and getting woken up:
async function handle_event() {
while (true) {
console.log("waiting...")
await wakeable(); // returns to event loop here
console.log("handle event woke up!");
}
}
handle_event(); // start in "background"
wakeup(); // wake it up
setTimeout(_ => { wakeup(); }, 500); // wake it up again after a delay
What's happening here is that when you call wakeable(), it returns a promise. That promise is constructed with an anonymous function (the one taking resolve as arg); the promise constructor synchronously calls that function, passing it the promise's resolve method. In our case, the function sets wakeup to another anonymous function that calls the original resolve; it's a closure so it has access to that resolve function even when it's called later. Then it returns the new promise.
In this demo, we then await on that promise; that puts the pending promise on a queue, saves the current function state, and returns, like a generator calling yield.
A promise is resolved when its resolve function is called; in this case calling wakeup() calls the promise's internal resolve() method, which triggers any .then methods on the next tick of the Javascript event loop (using the promise queue mentioned above). Here we use await, but .then(...) would work the same way'
So there's no magic; I/O and timeout promises work the same way. They keep a private registry of functions to call when the I/O event or timeout happens, and those functions call the promise's resolve() which triggers the .then() or satisfies the await.
By the way, unlike async in python, leaving a pending promise "open" when the process exits is perfectly fine in Javascript, and in fact this demo does that. It exits when there's no more code to run; the fact that the while loop is still "awaiting" doesn't keep the process running, because it's really just some closures stored in a queue. The event loop is empty, so the process exits (assuming it's in node.js -- in a browser it just goes back to waiting for events).
something() should be a method that returns a promise() or should be another method that is also notated with async.
function something(){
return new Promise(resolve,reject) {
//... call your database
// then
resolve(theResult)
// or
reject(theError)
}
}
The async and await for the most part are really just wrappers around promises. The await returns when the promise calls resolve, and throws an exception when the promise calls reject.
Your async function can return another promise; If it returns another value, it gets turned into a resolved promise with that value.

node.js asynchronous azure queue storage output binding

I am trying to create a JS function that reads a file from storage, writes its content to the database and pushes a result message to a queue. Reading and db processing are ok but nothing happens when writing to the queue.
The code is like:
process(file)
.then(() => {
context.bindings.outQueue = { file };
return context.log('File processing completed successfully');
})
.catch(err => context.log.error(err));
If I use context.done() instead, I get the following error:
Error: Choose either to return a promise or call 'done'. Do not use both in your script.
In either case no message is written to the queue.
What's wrong?
There are a couple of things that are most likely causing you issues. First, you should be returning within the main scope of the function. Not returning will cause your Function App to hang, which is probably why you tried adding context.done(). Second, you should not call context.done() when it could return prematurely. The error message you saw is trying to prevent you from doing so. Third, you should be returning your chained promise instead of calling context.done()`. This is what will let you write to the queue correctly.
To the first point: I think you're assuming that returning inside of .then(() => { ... }); is returning for the larger-scoped function. Remember that the lambda function passed into the .then() method is just returning a fulfillment value, which is used in order to pass the result of one promise to another promise (docs here). As a side-note, I'd suggest against returning context.log('File processing completed successfully'); (just call it, don't return it), as this is the same as returning void but leads to more confusing code.
To the second point: Calling context.done() inside of .then(() => { ... }); is calling a method to indicate that the main function from a fork'd asynchronous function. This is dangerous because theoretically, at any point after .then(() => { ... }); is defined, the main function code could be killed by that call to context.done().
To the third point: If you just call context.done() in the scope of the main function instead of returning the promise, you will have the opposite problem of point #2 (the main code will exit and stop execution of the fork'd asynchronous task before the asynchronous code has completed).
Here's an example of a simple JS function that waits on a promise and writes to a queue:
Example:
module.exports = function (context, req) {
context.log("Starting function");
// Mock Promise
let doWork = new Promise(
(resolve, reject) => {
setTimeout(resolve, 100, 'foo');
console.log("Work in promise");
}
);
doWork.then(() => {
context.log("Work AFTER promise");
context.bindings.queue1= "Queue message";
context.log("Message queued!");
}).catch(err => context.log.error(err));
// This is the correct scope to return promise
return doWork;
};

Categories