Javascript condition to use await of not - javascript

Is there a way I can write this block of code without all the repeated code for calling axios?
export const handler = async (event, useAwait) => {
const { path, data } = JSON.parse(event);
if (useAwait) {
return await axios(`https://example.com/api${path}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
} else {
// non-blocking
axios(`https://example.com/api${path}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
return true;
}
};

Put the promise into a variable, and then you can conditionally return it.
export const handler = async (event, useAwait) => {
const { data } = JSON.parse(event);
const prom = axios(`https://example.com/api${url}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
return useAwait ? (await prom) : true;
};
That said, you may as well return the Promise itself - awaiting something that you're returning immediately doesn't help since you're not immediately inside a try.
return useAwait ? prom : true;
But calling this function without returning the result looks like a bad idea because then you may get an unhandled rejection. You may wish to add a .catch to the Promise in that case to avoid that.

export const handler = (event) => {
const { path, data } = JSON.parse(event);
const prom = axios(`https://example.com/api${path}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
prom.catch(()=>{}); // prevent uncaught rejection
return prom;
}
If you don't test the return value of handler to be strictly equal to true, calling code can rely on promise objects being truthy in a conditional expression as needed, and the logic can be simplified to an ordinary function that returns a promise without using await or taking a useAwait parameter.
The dummy catch handler added to prom is to prevent generating an uncaught-rejected-promise error if the caller doesn't handle rejection of the axios promise, and can be omitted if uncaught rejection errors are acceptable. How calling code is meant to operate without waiting for the axios call completed is unclear and not covered here.
One of the reasons for suggesting major simplifications is the // non-blocking comment in the posted code. Async functions always return a promise object when called with no blocking effect, either to continued execution of calling code or browser operation.
The only effective difference between using await or not (in the post) is the value used to resolve the returned promise by handler.

Related

can async with fetch poll until a condition is met? (survive rejection)

Using fetch API and async/await, is it possible to continue polling indefinitely, regardless of availability of a URL? I anticipate that a URL might become available eventually, so I want to keep trying until a condition is met. Tried to come up with a minimum viable code sample and I'm not sure I pulled it off:
// this is just a placeholder. It will eventually be a function
// that evaluates something real.
// Assume validContinue gets updated elsewhere.
function shouldContinue() {
return validContinue;
}
async function wonderPoll(someUrl) {
// just a delay mechanism
function wait(ms = 1000) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
// the actual individual poll
async function pollingFunction(url) {
const response = await fetch(url, {
cache: 'no-store'
});
if (response.ok) {
return response;
} else {
Promise.reject(response);
}
}
// allegedly keep polling until condition is met.
// But the rejected Promise is breaking out!
while (shouldContinue()) {
await wait();
result = await pollingFunction(someUrl);
}
// when the fetch hits a rejected state, we never get here!
console.log('done with the while loop, returning last successful result')
return result;
}
const sampleUrl = 'https://get.geojs.io/v1/ip/country.json?ip=8.8.8.8';
const sampleUrl2 = 'http://totallybroken_fo_sho';
// swap the URL to test
wonderPoll(sampleUrl)
.then((result) => {
console.log('got a result', result)
})
.catch((err) => {
console.log('got an error', err)
});
I see what's happening (I think). The parent call ultimately executes the polling function, which rejects on the Promise. The condition to continue is still theoretically met, but the rejection breaks out of the While loop and sends to rejection directly up. This propagates all the way up to the catch method of the original/initial Promise. It doesn't even hit any code that would have come after the While loop in the case of resolved Promises.
What I don't know is how to prevent that from happening. I think I don't understand the syntax for intercepting and resolving the promise. When I replace Promise.reject in the response parser with Promise.resolve(response), it still ends up rejecting up to the top.
If the URL I provide is valid, it will continue until the condition is no longer met.
Here's a fiddle: https://jsfiddle.net/gregpettit/qf495bjm/5/
To use the fiddle, the "stop" button simulates the condition being met, and I've provided two different URLs that have to be manually swapped (by passing someUrl or someUrl2) to test.
Expected results:
with good URL, continuous polling (will have to dig into network in dev tools) until condition is met (by pressing Stop!) and then the calling function's 'then' can show the result.
with bad URL, continuous polling until condition is met, and then calling function's 'catch' shows the error
Actual results:
positive test case is OK
negative test case goes directly to the catch
You can try…catch it to prevent breaking out of loop.
while (shouldContinue()) {
try {
await wait();
result = await pollingFunction(someUrl);
} catch (e) {}
}
Change the code in while loop to try/catch so you can catch the error
result can hold a value when there's no error, or a reason when there is an error
Once the loop is stopped, you either return the value, or throw with the reason
As below
async function wonderPoll(someUrl) {
// just a delay mechanism
function wait(ms = 1000) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
// the actual individual poll
async function pollingFunction(url) {
const response = await fetch(url, {
cache: 'no-store'
});
if (response.ok) {
return response;
} else {
Promise.reject(response);
}
}
// allegedly keep polling until condition is met. But the rejected Promise is breaking out!
while (shouldContinue()) {
try {
await wait();
const value = await pollingFunction(someUrl);
result = {value};
} catch (reason) {
result = {reason};
}
}
// when the fetch hits a rejected state, we never get here!
console.log('done with the while loop, returning last successful result')
if (result.reason) {
throw result.reason;
}
return result.value;
}
Running example https://jsfiddle.net/twkbo9pg/
the example includes status in the result, but that is unnecessary (I borrowed code from my Promise.allSettled polyfill and forgot to remove that property)
you might want to check out observable streams! If you're going to have a lot of data coming in over time, that's rxjs's whole thing.
There's actually a few ways to do this if this feels janky (it kinda does haha).
import { ajax } from "rxjs/ajax";
import { duration } from "moment-timezone"; // I copied this from some old code... whatever.
import { catchError, map, share, switchMap } from "rxjs/operators";
const baseUrl = "http://foo.bar"
const base = (method, headers = {}) => ({
method,
headers: {
Accept: "application/json",
...headers,
},
crossDomain: true,
withCredentials: true,
})
const ajaxGet = url => ajax({ ...base("GET"), url })
export const userEpic = timer(0, duration(5, "minutes").asMilliseconds()).pipe(
switchMap(() =>
ajaxGet(`${baseUrl}/users`).pipe(
map(({ response }) => getUsersSuccess(response)),
catchError(e => of(getUsersError(e))),
)
),
share()
)
Two things
} else {
Promise.reject(response);
}
should return that. It's working "by accident" right now.
} else {
return Promise.reject(response);
}
Secondly, result = await pollingFunction(someUrl); might want to add .catch to it:
result = await pollingFunction(someUrl).catch(_=>null); or whatever can be tested for in the enclosing while
But I think you can simplify the whole thing thus:
export async function wonderPoll(someUrl) {
while (shouldContinue()) {
await wait();
const response = await fetch(someUrl, { cache: 'no-store' });
if (response.ok)
return response;
}
return Promise.reject(); // only if !shouldContinue()
}

Typescript - Using a value from then in function return block

I have this function:
function configureClient(): ApolloClient<ApolloCache<any>> {
let myToken = getOktaToken().then(token => {
return token
});
return new ApolloClient<InMemoryCache>({
uri: "someUrl",
cache: new InMemoryCache(),
headers: {
someAuth: myToken
}
});
}
I'm want someAuth: myToken in headers of the return block to be set however not sure how do this using this then block.
Also to add:
Generally, I would have done, let myToken = await getOktaToken() - however I'm unable to make the async function configureClient() as that ApolloClient<ApolloCache<any>> complains about some ES3. I presume this would have worked?
Type 'typeof DefaultClient' in not a valid async function return type in ES5/ES3 because it does not refer to a Promise compatible constructor value
Promises are asynchronous -- the callback you pass to then is evaluated after your function returns. You can make the whole configureClient function return a Promise, but that will mean you will have to change how you're using it elsewhere in your app (again, because at that point, the whole client will be initialized asynchronously).
function configureClient(): Promise<ApolloClient<ApolloCache<any>>> {
return getOktaToken().then(token => {
return new ApolloClient<InMemoryCache>({
uri: "someUrl",
cache: new InMemoryCache(),
headers: {
someAuth: token
}
});
});
}
// or the equivalent using async/await syntax
async function configureClient(): Promise<ApolloClient<ApolloCache<any>>> {
const token = await getOktaToken()
return new ApolloClient<InMemoryCache>({
uri: "someUrl",
cache: new InMemoryCache(),
headers: {
someAuth: token
}
});
}
You could delay rendering your app until the client is fetched. For example:
const App = () => {
const [client, setClient] = useState(null)
useEffect(() => {
configureClient().then(setClient)
}, [])
if (!client) {
return null
}
return (
<ApolloProvider client={client}>
...
</ApolloProvider>
)
}
If you have headers that need to be fetched asynchronously, though, the preferred way to do so is using apollo-link-context. You should stop using Apollo Boost or better yet migrate to the latest Apollo Client. Then you can configure the links for your client instance and add a Context Link as shown here.

How to pass information around when working with JavaScript Promise

At one point, we are calling our backend several times with fetch. Each call returns a promise, which we collect and pass to our responseHandler. This handler is supposed to dispatch redux actions when each response comes in. The action will be shaped differently based on two factors:
the response code of the server
the content of the request
so we have this
function save(objects) {
let promises = objects.map(object => {
let promise = fetch(url, {..., body: object});
});
responseHandler(promises);
}
function responseHandler(promises) {
for (let promise of promises) {
promise
.then(r => {
return r.json();
})
.then(body => dispatchSomeAction(body));
}
}
So far so good. But now we need info from inside of the save and from inside of the anonymous function that receives the response object. So I came up with the following approach. But it feels wrong to piggyback on the promise object. I can't say why.
function save(objects) {
let promises = objects.map(object => {
let promise = fetch(url, {..., body: object});
promise._objectType = object._type;
});
responseHandler(promises);
}
function responseHandler(promises) {
for (let promise of promises) {
promise
.then(r => {
promise._status = r.status;
return r.json();
})
.then(body => dispatchSomeAction({
status: promise._status,
type: promise._type, ...body
}));
}
}
As can be seen, every time I want a value of a scope to be available later, I place it on the promise object because that promise object is in all the other scopes. Is there a cleaner way?
Note: The backend returns an object that doesn't hold the _type property (actually I clear my objects of those flags before sending them to the backend. It's a frontend flag that I still require when the answer comes)
You must carry the object. Using ES6 structuring and destructuring is not very clumpsy:
function save(objects){
let promises = objects.map(object => {
return fetch(url, {..., body: object}).then(r => ({r,type:object._type}));
}
responseHandler(promises)
}
function responseHandler(promises){
for (let promise of promises){
promise
.then(({r,type}) => {
return r.json().then(body => dispatchSomeAction({status: r.status, type, ...body));
});
}
}
Note that you don't really need to do anything at all with the status, as r it was still in scope where it's used.
I would try something like the following
function save(objects){
let promises = objects.map(object) =>{
return fetch(url, {..., body: object}).then(r=>{
return {data: r, originalObject: object}
})
}
responseHandler(promises)
}
function responseHandler(promises){
for (let promise of promises){
promise
.then(r => {
return {data: r.data.json(), originalObject: r.originalObject)
.then(r=> dispatchSomeAction({status: r.data.status, type: r.originalObject.type, ...r.data.body));
}
}
you can tweak a little bit, say, without passing the whole object, just the type field.
A common pattern is to use a single global object (commonly called state or appData or similar) to store values and allow you to access them from within different closures.
Here's a contrived example of using this pattern to load a bunch of items, and then to reload the data for each item after clicking a "refresh" button:
const state = {}
getItemsPromise().then(response => {
state.items = response.items
})
$(refreshItemsButton).on('click', ()=>{
state.items.forEach(item => {
refreshItemPromise(item).then(response => {
state.items[item.id] == response.item
})
})
})

JavaScript Promise inside async/await function resolve final array of responses

I'm quite a newbie in JavaScript and in Promises.
I'm trying to build an array of objects that I get from an API.
To do so, I've build two functions in a file MyFile.js.
The first one returns a promise when an axios promise is resolved. It's
function get_items (url) {
return new Promise((resolve, reject) => {
let options = {
baseURL: url,
method: 'get'
}
axios(options)
.then(response => {
resolve(response.data)
})
.catch(error => {
reject(error.stack)
})
})
}
The second one looks like this:
let output = []
let next_url = 'https://some_url.com/api/data'
async function get_data () {
try {
let promise = new Promise((resolve, reject) => {
if (next_url) {
get_items(next_url)
.then(response => {
output.push(...response.results)
if (response.next) {
next_url = response.next
console.log('NEXT_URL HERE', next_url)
get_data()
} else {
console.log('else')
next_url = false
get_data()
}
})
.catch(error => {
reject(error.stack)
})
} else {
console.log('before resolve')
resolve(output)
}
})
return await promise
} catch(e) {
console.log(e)
}
}
It's where I'm grinding my teeth.
What I think I understand of this function, is that:
it's returning the value of a promise (that's what I understand return await promise is doing)
it's a recursive function. So, if there is a next_url, the function continues on. But if there is not, it gets called one last time to go into the else part where it resolves the array output which contains the results (values not state) of all the promises. At least, when I execute it, and check for my sanity checks with the console.log I wrote, it works.
So, output is filled with data and that's great.
But, when I call this function from another file MyOtherFile.js, like this:
final_output = []
MyFile.get_data()
.then(result => {
console.log('getting data')
final_output.push(...result)
})
it never gets into the then part. And when I console.log MyFile.get_data(), it's a pending promise.
So, what I would like to do, is be able to make get_data() wait for all the promises result (without using Promise.all(), to have calls in serie, not in parallel, that would be great for performances, I guess?) and then be able to retrieve that response in the then part when calling this function from anywhere else.
Keep in mind that I'm really a newbie in promises and JavaScript in general (I'm more of a Python guy).
Let me know if my question isn't clear enough.
I've been scratching my head for two days now and it feels like I'm running in circle.
Thanks for being an awesome community!
This is a bit untested
const api_url = 'https://some_url.com/api/data';
get_data(api_url).then((results) => {
console.log(results);
}).catch((error) => {
// console.error(error);
});
function get_items (url) {
const options = {
baseURL: url,
method: 'get'
};
return axios(options).then((response) => response.data);
}
async function get_data(next_url) {
const output = [];
while (next_url) {
const { results, next } = await get_items(next_url);
output.push(...results);
next_url = next;
}
return output;
}
Basically it makes things a bit neater. I suggest to look at more examples with Promises and the advantage and when to ease await/async. One thing to keep in mind, if you return a Promise, it will follow the entire then chain, and it will always return a Promise with a value of the last then.. if that makes sense :)
There are a few problems. One is that you never resolve the initial Promise unless the else block is entered. Another is that you should return the recursive get_data call every time, so that it can be properly chained with the initial Promise. You may also consider avoiding the explicit promise construction antipattern - get_items already returns a Promise, so there's no need to construct another one (same for the inside of get_items, axios calls return Promises too).
You might consider a plain while loop, reassigning the next_url string until it's falsey:
function get_items (baseURL) {
const options = {
baseURL: url,
method: 'get'
}
// return the axios call, handle errors in the consumer instead:
return axios(options)
.then(res => res.data)
}
async function get_data() {
const output = []
let next_url = 'https://some_url.com/api/data'
try {
while (next_url) {
const response = await get_items(next_url);
output.push(...response.results)
next_url = response.next;
}
} catch (e) {
// handle errors *here*, perhaps
console.log(e)
}
return output;
}
Note that .catch will result in a Promise being converted from a rejected Promise to a resolved one - you don't want to .catch everywhere, because that will make it difficult for the caller to detect errors.
Another way of doing it is to not use async at all and just recursively return a promise:
const getItems = (url) =>
axios({
baseURL: url,
method: 'get',
}).then((response) => response.data);
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl).then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl)
.catch(e=>Promise.reject(e.stack));//reject with error stack
};
As CertainPerformance noted; you don't need to catch at every level, if you want getData to reject with error.stack you only need to catch it once.
However; if you had 100 next urls and 99 of them were fine but only the last one failed would you like to reject in a way that keeps the results so far so you can try again?
If you do then the code could look something like this:
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl)
.catch(e=>Promise.reject([e,result]))//reject with error and result so far
.then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl);//do not catch here, just let it reject with error and result
};

RxJS error handling

I've got an issue with RxJS swallowing errors. So in my case I've got something like this:
function funcThatThrowsError(fn, ..args) {
return fn.bind(fn, ...args);
}
function fetchItems() {
Observable.fromPromise(
reqwest({
url: `${API_ROOT}${url}`,
method,
data,
contentType: "application/json"
})
).map(funcThatThrowsError(null, "someValue"))
}
const observableA = fechItems();
const observableB = ... ;
Observable
.combineLatest(
observableA,
observableB,
() => { }
)
.forEach(
() => { }, // success
(err) -> console.log(err.stack);
)
So basically I'm deliberately passing a null value as the fn parameter which causes the funcThatThrowsError to throw an exception.
The problem is that the error callback is not invoked in this case. Rather RxJS will use it's own thrower function
function thrower(e) {
throw e;
}
What's the best practise to deal with this scenario. I feel that there is something that I'm missing.
The exception is happening outside of the Observable. You are raising it while creating a function that you want to pass into the observable.
If you want the error handler at the end of the chain to handle it, you have to raise it inside of the operator chain:
function fetchItems() {
Observable.fromPromise(
request({
url: `${API_ROOT}${url}`,
method,
data,
contentType: "application/json"
})
).map(funcThatThrowsError(content => throw new Error(content), "someValue"))
}

Categories