How to fully await RxJS Observable - javascript

I am using a library which uses RxJS and I am brand new to RxJS so, although I have googled and tried to do my own research I am still a bit lost.
This library has a function that returns an observable. That observable really only returns a single message when subscribed but I need to wait for the data contained within that message before my code can continue.
I have read about chaining observables and all of that but what I am really after is a very simple async/await pattern. I notice there is a .toPromise() on the observable which you can await on, which is fine, however it appears that promise is resolved before the full execution of my next() function which results in a timing issue with the variables it was setting.
Does that sound correct? How can I simply await an observable AND all its side effects such as calls to next()?
What I have ended up doing, which works reliably but seems overkill is to wrap in a promise as demonstrated below. Is there a more succinct way?
const result = await new Promise((resolve, reject) => {
const s = jupyter.sessions.create(this._serverConfig,
{
kernel: {name: this._kernelName},
name: this._sessionName,
path: '/path',
type: 'notebook'
});
s.pipe(take(1)).subscribe({
next(x) {
resolve({sessionId: x.response.id, kernelId: x.response.kernel.id});
},
error(err) {
reject(err);
},
});
});
this._sessionId = result.sessionId;
this._kernelId = result.kernelId;
For completeness, this is the old code that is NOT working. In this code the await completes in a race condition with the next function. That is, randomly both sessionId and kernelId are undefined as per the log message that follows the await
s.subscribe({
next(x) {
console.log(x);
sessionId = x.response.id;
kernelId = x.response.kernel.id;
},
error(err) {
console.error('Got an error from kernel creation:')
console.error(err);
},
})
.add(() => {
console.log('Connection subscription exiting');
});
await s.toPromise();
console.log(`Found session id ${sessionId} and kernel id ${kernelId}`);

Related

return the answer to another promise file

I have an isolated scene. And there is a promise in a separate file. But my scene does not want to wait for an answer. And continue to work. And continues to run, how do I make it wait for an answer, and continued to work
file: a
async function apiExchangerate(country, amount) {
return new Promise(function (resolve, reject) {
axios.get(``, {
headers: { "Accept-Encoding": "gzip,deflate,compress" },
}).then(
(response) => {
var result = String(response.data.result).replace(/\..*/, '');
console.log('Processing Request');
resolve(result);
},
(error) => {
reject(error);
}
);
});
}
module.exports = apiExchangerate
file: b
let request_currency = await apiExchangerate(country,amount) // you have to wait for a response, and then continue the work
I want my function to wait for a response and continue executing the script. On the Internet, I have not found an answer to my question.
P.s it doesn't work - What is the explicit promise construction antipattern and how do I avoid it?
You're wrapping Promises in Promises for no real reason. One of the reasons why this is an anti-pattern is because it's very easy to get confused by that and to mis-handle resolving/rejecting those Promises.
(My guess is that somewhere you're returning a Promise which resolves to... a Promise. And you're not double-awaiting it so you never resolve the actual Promise.)
Don't over-design it. Simplify the function. axios.get already returns a Promise, and the function is already async, use those:
async function apiExchangerate(country, amount) {
let response = await axios.get(``, { headers: { "Accept-Encoding": "gzip,deflate,compress" } });
let result = String(response.data.result).replace(/\..*/, '');
console.log('Processing Request');
return result;
}
Then what you have is a simple async function which will internally await its own operations. And you can await that function:
let request_currency = await apiExchangerate(country, amount);

How exactly works this async\await code in my Angular application? Why the colled method doesn't explicitly return a Promise?

I am not so into RxJS and I am finding some problems to understand this piece of code retrieved into an Angular project on which I am working.
First of all into a component TypeScript code I have this method:
async post(): Promise<void> {
this.submitted.next(true);
try {
await this.setAddress();
this.activeModal.close();
} catch (e) {
console.error('Storage upload error', e);
this.submitted.next(false);
}
}
As you can see this method have async prefix because into the try block it contains these 2 rows:
await this.setAddress();
this.activeModal.close();
from what I have understand (please correct me if I am doing wrong assertion) basically the await in front of this.setAddress() it means: await that this method call end, when it is completed executed the following operation (that in this case close a modal window).
From what I have understand it replave the then() method handling a Promise resolution. Is it correct or not?
So my doubt is: have my setAddress() method return a Promise? In my specific case setAddress() method is used to call a service method saving some data on the database and have this code:
async setAddress(): Promise<void> {
try {
const c: Address = {
companyName:this.addressFormGroup.get('companyName').value,
street: this.addressFormGroup.get('street').value,
city: this.addressFormGroup.get('city').value,
zipCode: this.addressFormGroup.get('zipCode').value,
notes: this.addressFormGroup.get('notes').value,
};
//save/update record
await this.userService.setUserAdresss(this.currentUserUID,this.addressType,c);
this.success = true;
if (!this.isEditMode) {
this.addressFormGroup.reset();
}
} catch (e) {
console.error(e);
} finally {
this.submitted.next(false);
}
}
And here I have a lot of doubts on how it works...ok the method signature:
async setAddress(): Promise<void> {
seems to return a Promise (why ? what it means?). But where is it effectivelly returning a Promise? In the code of this method I can't find that it is returning a Promise nowhere. It seems to me that it is returning nothing because it is not containing the return statement !!!
My only interpretation is the following one (but it is my idea and probably totally wrong): also if it is not explicitly returning nothing it have a Promise as method returned type. So it means that at the end of the method execution TypeScript automatically return an "empty" Promise that simply means: "method execution completed".
I am absolutly not sure, this is the only explaination that I can give to this code.
How it wxactly works?
Your assumptions are correct.
A function declared with the async keyword will return a Promise that is completed when the function has finished its execution.
The await keyword is equivalent to using the then method with the remaining lines of codes as the callback function.
Using try/catch/finally arround an await is equivalent to using the catch/finally method on the promise.
This is your code written with promises instead of async/await :
post(): Promise<void> {
this.submitted.next(true);
return this.setAddress()
.then(() => this.activeModal.close())
.catch((e) => {
console.error('Storage upload error', e);
this.submitted.next(false);
});
}
setAddress(): Promise<void> {
const c: Address = {
companyName:this.addressFormGroup.get('companyName').value,
street: this.addressFormGroup.get('street').value,
city: this.addressFormGroup.get('city').value,
zipCode: this.addressFormGroup.get('zipCode').value,
notes: this.addressFormGroup.get('notes').value,
};
//save/update record
return this.userService.setUserAdresss(this.currentUserUID,this.addressType,c)
.then(() => {
this.success = true;
if (!this.isEditMode) {
this.addressFormGroup.reset();
}
})
.catch((e) => console.error(e))
.finally(() => this.submitted.next(false));;
}

Timeout exceeded for Mocha promises

I have a async function that awaits a promise which resolves when it receives some 'data'. However, when I run the test, I get a Error: Timeout of 300000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
Here is my code snippet, I am using this in truffle to test solidity contracts :
contract("Test", async (accounts) => {
it("test description", async () => {
let first = await getFirstEvent(oracle.LogResult({fromBlock:'latest'}));
let second = await getFirstEvent(oracle.LogResult({fromBlock:'latest'}));
Promise.all([first,second]);
//some assertion code
});
const getFirstEvent = (_event) => {
return new Promise((resolve, reject) => {
_event.once('data', resolve).once('error', reject)
});
}
});
Isn't the promise resolving ? I can see 'data' coming back in the callback because I am emitting the callback event in the solidity code I am testing.
I managed to resolve this issue, so posting it here so that others can use the approach.
I created a Promise that times out after a duration we can set :
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {a
reject(new Error('Request timed out'));
}, 200000);
})
Then, I race the timeoutPromise with the Promise which is fetching data, like this for the case I posted :
Promise.race([getFirstEvent(oracle.LogResult({fromBlock:'latest'})), timeoutPromise]);
It looks to me like there's a few things wrong here.
First of all, your function isn't returning anything i.e. it should be return Promise.all([first, second]);.
Secondly, if the goal of the promise.all is to execute the promises in parallel, then that's not what it's doing here because you already have await statements on those function calls above. What you are looking for here would be:
return await Promise.all([
getFirstEvent(oracle.LogResult({fromBlock:'latest'}),
getFirstEvent(oracle.LogResult({fromBlock:'latest'})]);
Now in terms of the promise not resolving, I'm assuming the event is generated from oracle.LogResult(). In this case, what you'd want to do is setup your promises to listen for the event first, for example:
let first = getFirstEvent();
let second = getSecondEvent();
Now you have 2 promises that are listening for the events. Next, you generate the event:
oracle.LogResult({ fromBlock: 'latest' });
oracle.LogResult({ fromBlock: 'latest' });
Finally, you ensure you wait on the result of the promises:
return await Promise.all([first, second]);

Axios POST resolves to undefined

I started switching my project from standard Fetch API to Axios library. Axios seemed great, with all the interceptors, custom instances, etc. The problem started with POST requests.
I have an custom axios instance defined as:
export const axiosInstance = axios.create({
baseURL: API_URL,
timeout: 10000
});
Im using it for most of my API calls and most of them are fine, except one that I got stuck on for a incredibly frustrating amount of time.
Using POST request, it seems that the Promise is resolved into undefined.
Lets take a look at a code:
export async function saveIncomes(incomes) {
const { added, changed } = incomes;
const add_res = axiosInstance.post(`${INCOMES_URL}`, added).then(console.log);
const change_responses = [];
for (let changed_row of changed) {
change_responses.push(
axiosInstance.put(`${INCOMES_URL}${changed_row.id}/`, changed_row)
);
}
let finalRes = [];
try {
finalRes = await axios.all([add_res, ...change_responses]);
} catch (e) {
console.log(e);
}
return finalRes;
}
This function takes two arrays of incomes - added and changed (since two http methods),
prepare all promises for them (I have bulk POST but not PUT on my API) and call axios.all to run them concurrently. Now, the fun begins.
When I post the correct data that is validated by API and returns 201 created, all is fine, the Promise resolves to current axios Response object, but when the data is incorrect and status is 400, it resolves to undefined.
Example:
axios.all([p1, p2, p3]) // correct data
-> [<response1>, <response2>, <response3>
axios.all([p1, p2, p3]) // incorrect data
-> [undefined, undefined, undefined]
It doesnt throw an error, it resolves, but to no avail.
The browser however gets the data correctly (I mean, its status 400, but there IS an response body).
I have no clue what to do anymore, I'm new to axios but it looked less problematic then it is now.
My frontend app is on React.js, some parts of it still uses fetch API, because its a work in progress.
Backend is in python Django with DRF.
EDIT:
Also, I'm using interceptors heres the code:
export function setResponseInterceptor({ onSuccess, onError }) {
if (!onSuccess) {
onSuccess = response => response;
}
axiosInstance.interceptors.response.use(response => {
if (isHandlerEnabled(response.config)) {
response = onSuccess(response);
}
console.log(response);
return response;
}, onError);
}
export function setRequestInterceptor({ beforeSend, onError }) {
if (!beforeSend) {
beforeSend = cfg => cfg;
}
axiosInstance.interceptors.request.use(cfg => {
if (isHandlerEnabled(cfg)) {
cfg = beforeSend(cfg);
}
return cfg;
}, onError);
}
Axios call Promise.all() on axios.all(), which run promises asynchroniously. Looking at MDN definition of promises .all reject you can see the following :
Using Promise.all
Promise.all waits for all fulfillments (or the first rejection).
Rejection
If any of the passed-in promises reject, Promise.all asynchronously rejects with the value of the promise that rejected, whether or not the other promises have resolved.
As your API return 401, it return the rejection of the failling promise, ignoring the others.
Catch a rejection
Using .catch, you will receive a unique promise rejection as argument and should be able to read it's value.
// Using .catch:
Promise.all([p1, p2, p3, p4, p5])
.then(values => {
console.log(values);
})
.catch(error => {
console.error(error.message)
});
Looking at your code, you need to make sure your function saveIncomes handle properly this behaviour.

Is creating a new promise with a async function call bad practice?

Snippets are from a node.js and mongoDB CRUD application.Github repo for full code. The code is working fine but unsure if my structure and use of promises and async await are bad practice.
handlers._newbies = {};
handlers._newbies.post = (parsedReq, res) => {
const newbie = JSON.parse(parsedReq.payload);
databaseCalls.create(newbie)
.then((result) => {
res.writeHead(200,{'Content-Type' : 'application/json'});
const resultToString = JSON.stringify(result.ops[0]);
res.write(resultToString);
res.end();
})
.catch(err => console.log(err));
};
const databaseCalls = {};
databaseCalls.create = (newbie) => {
return new Promise(async (resolve, reject) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
console.log("Connected correctly to server");
const db = client.db('Noob-List');
const result = await db.collection('newbies').insertOne(newbie);
client.close();
resolve(result);
} catch(err) {
console.log(err);
}
});
};
When the node server gets a POST request with the JSON payload, it calls the handlers._newbies.post handler which takes the payload and passed it to the
const newbie = JSON.parse(parsedReq.payload);
databaseCalls.create(newbie)
call. I want this database call to return a promise that holds the result of the db.collection('newbies').insertOne(newbie);
call. I was having trouble doing this with just returning the promise returned by the insertOne because after returning I cant call client.close();.
Again maybe what I have done here is fine but I haven't found anything online about creating promises with promises in them. Thank you for your time let me know what is unclear with my question.
It is considered an anti-pattern to be wrapping an existing promise in a manually created promise because there's just no reason to do so and it creates many an opportunities for error, particular in error handling.
And, in your case, you have several error handling issues.
If you get an error anywhere in your database code, you never resolve or reject the promise you are creating. This is a classic problem with the anti-pattern.
If you get an error after opening the DB, you don't close the DB
You don't communicate back an error to the caller.
Here's how you can do your .create() function without the anti-pattern and without the above problems:
databaseCalls.create = async function(newbie) {
let client;
try {
client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
console.log("Connected correctly to server");
const db = client.db('Noob-List');
return db.collection('newbies').insertOne(newbie);
} catch(err) {
// log error, but still reject the promise
console.log(err);
throw err;
} finally {
// clean up any open database
if (client) {
client.close();
}
}
}
Then, you would use this like:
databaseCalls.create(something).then(result => {
console.log("succeeded");'
}).catch(err => {
console.log(err);
});
FYI, I also modified some other things:
The database connection is closed, even in error conditions
The function returns a promise which is resolved with the result of .insertOne() (if there is a meaningful result there)
If there's an error, the returned promise is rejected with that error
Not particularly relevant to your issue with promises, but you will generally not want to open and close the DB connection on every operation. You can either use one lasting connection or create a pool of connections where you can fetch one from the pool and then put it back in the pool when done (most DBs have that type of feature for server-side work).

Categories