Google ReCaptcha v3 taking as much as 15-20 seconds to respond - recaptcha

I am trying to implement ReCaptcha v3 on my site. I expect the code for it to execute in under 1 second. In some tests its taken up to 20 seconds.
const t = Date.now()
console.log(t)
const captchaCode = await doRecaptcha()
const t2 = Date.now()
const t3 = t2 - t
console.log(t3, "time elapsed")
In my latest test the value of t3 was 15011 which is 15 seconds! In another test I got 23522 'time elapsed'. Yet another test gives 22926 for 22.9 seconds.
What's up with that?
Here is the function
async function doRecaptcha() {
return new Promise((resolve, reject) => {
try {
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
grecaptcha.ready(function () {
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
grecaptcha
.execute(googleRecaptchaSiteKey, { action: "submit" })
.then(function (captchaToken) {
resolve(captchaToken)
})
})
} catch (err) {
reject(err)
}
})
}
And I load the script for it like this
<svelte:head>
<script
src="https://www.google.com/recaptcha/api.js?render={googleRecaptchaSiteKey}"
async></script>
</svelte:head>
I used to have defer on that script but I tried removing it, thinking maybe the defer was to blame.

Related

useEffect executes with stale information

If I have a useEffect hook like this:
const [text, setText] = useState("");
useEffect(() => {
async function run() {
// fetch new text when some prop changes
const newText = await fetchText();
// to exaggerate the effect of waiting,
// let's sleep for two seconds
await new Promise((r) => setTimeout(r, 2000));
setText(newText);
}
run();
}, [some_prop]);
Every time some_prop changes, I fetch new text from an endpoint. Suppose run takes 3 seconds to finish. If the value of some_prop changes more often that, useEffect will still resolve all the async calls to run. Suppose some_prop changes from a -> b -> c -> d -> e in one second. I would like to see the value of text go smoothly from a to e, but it will flash through all the intermediate values (b -> c -> d) as the calls to run finish. How can I throw away those intermediate calls?
Add a let variable (cancel) to the useEffect block, and if the useEffect is called, set cancel to true. If cancel is called, avoid setting the state.
Note: The obvious solution in the example is to cancel the timeout, but the timeout simulates an api call.
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
<script type="text/babel">
const { useState, useEffect } = React;
const Demo = () => {
const [num, setN] = useState(0);
const [text, setText] = useState('Text: ');
useEffect(() => {
let cancel = false;
async function run() {
// simulate api call
await new Promise(r => setTimeout(r, 2000));
if(cancel) return;
setText(`Text: ${num * 10}`);
}
run();
return () => {
cancel = true;
};
}, [num]);
return (
<div>
<button onClick={() => setN(n => n + 1)}>{num}</button>
<div>{text}</div>
</div>
);
}
ReactDOM
.createRoot(root)
.render(<Demo />);
</script>
One caveat is that the api is getting called needlessly, and we don't cancel the requests. We can cancel the actual request, by using fetch or any a library that supports cancelling (axios for example).
If you're using fetch, you just need to pass the signel from an abort controller.
fetch(url, { signal })
Read the docs of other libraries to see how they can be cancelled.
This example uses an abort controller to cancel the timeout.
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
<script type="text/babel">
const { useState, useEffect } = React;
const Demo = () => {
const [num, setN] = useState(0);
const [text, setText] = useState('Text: ');
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
async function run() {
// simulate api call
await new Promise(r => {
const timeout = setTimeout(r, 2000);
signal.addEventListener('abort', () => {
clearTimeout(timeout);
});
});
setText(`Text: ${num * 10}`);
}
run();
return () => {
controller.abort();
};
}, [num]);
return (
<div>
<button onClick={() => setN(n => n + 1)}>{num}</button>
<div>{text}</div>
</div>
);
}
ReactDOM
.createRoot(root)
.render(<Demo />);
</script>

google actions webhook promise await - what am I doing wrong?

I am struggling with getting JS to wait for a process to finish before continuing
With a simple wait, the process continues while it is meant to wait
const functions = require('firebase-functions');
const app = conversation();
// wait ms milliseconds
function wait(ms) {
console.log("in the wait");
return new Promise(r => setTimeout(r, ms));
}
async function getData() {
console.log(`start wait `);
await wait(5000);
console.log(`end wait `);
}
app.handle('findit', conv => {
console.log(`-----> handle activated `);
console.log(`-----> BEFORE call getdata`);
getData();
console.log(`--------END OF HANDLE ----------------------`);
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);```
[Timing logs][1]
[1]: https://i.stack.imgur.com/96Ydv.png
const app = conversation();
// wait ms milliseconds
function wait(ms) {
console.log("in the wait");
return new Promise(r => setTimeout(r, ms));
}
async function getData() {
console.log(`start wait `);
await wait(5000);
console.log(`end wait `);
}
app.handle('findit', async conv => {
console.log(`-----> handle activated `);
console.log(`-----> BEFORE call getdata`);
getData();
console.log(`--------END OF HANDLE ----------------------`);
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);```
the issue was the conv was not async - the addition of the async to the app.handle('findit', async conv => { fixed the issue.

Delay batch of observables with RxJS

I perform http requests to my db and have noticed that if I send all the requests at once, some of them will get a timeout errors. I'd like to add a delay between calls so the server doesn't get overloaded. I'm trying to find the RxJS solution to this problem and don't want to add a setTimeout.
Here is what I currently do:
let observables = [];
for(let int = 0; int < 10000; int++){
observables.push(new Observable((observer) => {
db.add(doc[int], (err, result)=>{
observer.next();
observer.complete();
})
}))
}
forkJoin(observables).subscribe(
data => {
},
error => {
console.log(error);
},
() => {
db.close();
}
);
You can indeed achieve this with Rxjs quite nicely. You'll need higher order observables, which means you'll emit an observable into an observable, and the higher order observable will flatten this out for you.
The nice thing about this approach is that you can easily run X requests in // without having to manage the pool of requests yourself.
Here's the working code:
import { Observable, Subject } from "rxjs";
import { mergeAll, take, tap } from "rxjs/operators";
// this is just a mock to demonstrate how it'd behave if the API was
// taking 2s to reply for a call
const mockDbAddHtppCall = (id, cb) =>
setTimeout(() => {
cb(null, `some result for call "${id}"`);
}, 2000);
// I have no idea what your response type looks like so I'm assigning
// any but of course you should have your own type instead of this
type YourRequestType = any;
const NUMBER_OF_ITEMS_TO_FETCH = 10;
const calls$$ = new Subject<Observable<YourRequestType>>();
calls$$
.pipe(
mergeAll(3),
take(NUMBER_OF_ITEMS_TO_FETCH),
tap({ complete: () => console.log(`All calls are done`) })
)
.subscribe(console.log);
for (let id = 0; id < NUMBER_OF_ITEMS_TO_FETCH; id++) {
calls$$.next(
new Observable(observer => {
console.log(`Starting a request for ID "${id}""`);
mockDbAddHtppCall(id, (err, result) => {
if (err) {
observer.error(err);
} else {
observer.next(result);
observer.complete();
}
});
})
);
}
And a live demo on Stackblitz: https://stackblitz.com/edit/rxjs-z1x5m9
Please open the console of your browser and note that the console log showing when a call is being triggered starts straight away for 3 of them, and then wait for 1 to finish before picking up another one.
Looks like you could use an initial timer to trigger the http calls. e.g.
timer(delayTime).pipe(combineLatest(()=>sendHttpRequest()));
This would only trigger the sendHttpRequest() method after the timer observable had completed.
So with your solution. You could do the following...
observables.push(
timer(delay + int).pipe(combineLatest(new Observable((observer) => {
db.add(doc[int], (err, result)=>{
observer.next();
observer.complete();
}))
}))
Where delay could probably start off at 0 and you could increase it using the int index of your loop by some margin.
Timer docs: https://www.learnrxjs.io/learn-rxjs/operators/creation/timer
Combine latest docs: https://www.learnrxjs.io/learn-rxjs/operators/combination/combinelatest
merge with concurrent value:
mergeAll and mergeMap both allow you to define the max number of subscribed observables. mergeAll(1)/mergeMap(LAMBDA, 1) is basically concatAll()/concatMap(LAMBDA).
merge is basically just the static mergeAll
Here's how you might use that:
let observables = [...Array(10000).keys()].map(intV =>
new Observable(observer => {
db.add(doc[intV], (err, result) => {
observer.next();
observer.complete();
});
})
);
const MAX_CONCURRENT_REQUESTS = 10;
merge(...observables, MAX_CONCURRENT_REQUESTS).subscribe({
next: data => {},
error: err => console.log(err),
complete: () => db.close()
});
Of note: This doesn't batch your calls, but it should solve the problem described and it may be a bit faster than batching as well.
mergeMap with concurrent value:
Perhaps a slightly more RxJS way using range and mergeMap
const MAX_CONCURRENT_REQUESTS = 10;
range(0, 10000).pipe(
mergeMap(intV =>
new Observable(observer => {
db.add(doc[intV], (err, result) => {
observer.next();
observer.complete();
});
}),
MAX_CONCURRENT_REQUESTS
)
).subscribe({
next: data => {},
error: err => console.log(err),
complete: () => db.close()
});

When you subscribe with rxjs, how do you signal to your test if it fails?

I am a complete beginner.
The issue I am having is that once I throw an error in rxjs observable, my test doesn't know about it. When I am subscribing in a test, and it fails within rxjs it just throws an error and I need to notify my test that the error occurred. Here's a more simple example that shows that "test failed" is never printed.
import { sample } from "rxjs/operators";
const source = interval(1000);
// sample last emitted value from source every 2s
// output: 2..4..6..8..
const example = source.pipe(sample(interval(2000)));
async function test_runner() {
setup();
try {
await test();
console.log("test succeeded");
} catch (e) {
console.log("test failed");
}
}
async function setup() {
console.log("setup");
const subscribe = example.subscribe((val) => {
console.log(val);
if (val === 4) { throw Error("error!"); }
});
}
async function test() {
console.log("test");
await waitMs(10000);
}
test_runner();
async function waitMs(waitTime: number): Promise<void> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, waitTime);
});
}
Is there a way to handle this? I appreciate any help.
If you want to test rx streams one of the best ways is to use marbles diagram.
That's what ngrx uses for effects testing.
https://www.npmjs.com/package/jasmine-marbles
https://github.com/ngrx/platform/blob/master/docs/effects/testing.md
With marbles diagram you can write style where you expect emit / error and to assert it.
For example syntax hot('---#') means that after 30ms there's an error in the stream.
When you subscribe you can pass functions to:
process items emitted by the stream
process an error
process a completion signal
You can use that in your tests too:
describe('when a stream emits an error', () => {
it('should call your error handler', () => {
const stream$ = throwError('wat?!');
stream$.subscribe({ error: (err) => {
chai.expect(err === 'wat?!').to.be.true;
}});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.min.js"></script>
<script src="https://unpkg.com/chai/chai.js"></script>
<script src="https://unpkg.com/mocha/mocha.js"></script>
<script>const {throwError} = rxjs;</script>
<div id="mocha"></div>
<script class="mocha-init">mocha.setup('bdd');</script>
<script class="mocha-exec">mocha.run();</script>

Why does this little rxjs-code-snippet using the concat-operator throw an error?

Preconditions:
The ref.getDownload() returns an Observable which only can be subscribed, if the
task.snapshotChanges()-Observable completed.
This code-snippet works:
task.snapshotChanges().subscribe({
complete: () => {
ref.getDownloadURL().subscribe((downloadUrl) => console.log(downloadUrl));
}
});
This code-snippet does NOT work:
concat(
task.snapshotChanges(),
ref.getDownloadURL()
).pipe(
last()
).subscribe((downloadUrl) => console.log(downloadUrl));
getDownloadUrl throws an error (404 file not found), because it seems
ref.getDownloadUrl is subscribed to early.
Why subscribes the ref.getDownloaded()-Observable and does not wait until task.snapshotChanges() completes? The concat-operator should ensure this behaviour.
Or am I wrong?
The function ref.getDownloadURL() is called when the concat(..) Observable is created. See:
const { of, concat } = rxjs;
const { delay } = rxjs.operators;
const fetch1 = () => { console.log('run fetch1'); return of('from 1').pipe(delay(2000)) }
const fetch2 = () => { console.log('run fetch2'); return of('from 2').pipe(delay(2000)) }
concat(fetch1(), fetch2()).subscribe(console.log);
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
ref.getDownloadURL() seems to query the database directly when it gets called and not when the Observable it returns gets subscribed to.
You can wrap ref.getDownloadURL() with defer to only execute it when the Observable is subscribed to.
const { of, concat, defer } = rxjs;
const { delay } = rxjs.operators;
const fetch1 = () => { console.log('run fetch1'); return of('from 1').pipe(delay(2000)) }
const fetch2 = () => { console.log('run fetch2'); return of('from 2').pipe(delay(2000)) }
concat(fetch1(), defer(() => fetch2())).subscribe(console.log);
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
Also see my answer here https://stackoverflow.com/a/57671521/9423231

Resources