In JavaScript, I could constantly fetch data without an explicit request from the user by calling a function fetchData() every five seconds using setInterval(function() { fetchData() }, 5000); and this is an incredibly useful tool for me. Is there a similar equivalent in Flutter?
This can be achieved by something like this.
import 'dart:async';
main() {
const fiveSeconds = const Duration(seconds: 5);
// _fetchData() is your function to fetch data
Timer.periodic(fiveSeconds, (Timer t) => _fetchData());
}
Timer() and Timer.periodic() work the same way. They take duration as a parameter and an optional callback function.
Timer(const Duration(seconds: 5), () {
// these lines would be executed every 5s.
});
Related
I am working on an application that makes multiple API calls. The API uses pagination, which limits the number of results and returns a cursor to be used with subsequent calls if more data is needed.
Here is my current code:
async function getData(url, cursor) {
// Adds the cursor to the API URL.
cursorUrl = url + "&cursor=" + cursor;
// Uses fetch to make the API call.
result = await fetch(cursorUrl);
return result.json()
}
async function getAllData() {
// Page limit
const pageLimit = 3;
// Number of "pages" of found. Default page size is 50.
let pagesSorted = 0;
// The cursor to use if finding multiple pages. Default is null for first call.
let passedCursor = "";
// Array to hold cumulitive data.
let cumulitiveData = [];
while (passedCursor != null && pagesSorted < pageLimit) {
await getData(appendedUrl, passedCursor)
.then(data => {
// Add data to cumulitiveData array
cumulitiveData.push(data)
// Add one to total pages sorted.
pagesSorted += 1;
// Record cursor for next call
passedCursor = data["cursor"];
if (passedCursor == null) {
console.log("Reached end of responses from API.")
} if (pagesSorted >= pageLimit) {
console.log("Page limit reached.")
}
});
}
return cumulitiveData
}
This code works, but it blocks anything else from happening (like other API calls) until it is done. It can sometimes take a long time, and I'd like to perform other actions while it is working.
I have been stuck on this for so long, any help is greatly appreciated. I'm new to programming, so any critique on my code overall is welcome.
As implied by jfriend00 in the question comments, that is the basics of JavaScript asynchronicity.
Unlike other languages, where a code blocks a thread, and you need to "spawn" new threads to achieve parallel tasks, in JavaScript runtime, asynchronous tasks are queued, and when they are waiting for an external event (here a response from your external API), other tasks can run.
For example, if you have:
setInterval(() => console.log("every 1 second"), 1000);
getAllData();
async function getAllData() {
while (notDone) {
console.log("getData");
await getData();
}
}
...you would see a log like:
getData
every 1 second
every 1 second
getData
every 1 second
every 1 second
getData
every 1 second
Showing that asynchronous tasks are interleaved.
I’m trying to solve a simple problem here but I have no idea what direction to take.
getAuthNumber() // returns a promise with a number (eg 98765)
// response times can be 5s-20s
<div class=“auth”> </div>
//code
let counter = 0;
let el = document.getElementsbyClassName(“auth”)[0];
let func = setInterval(function(){
counter++;
getAuthNumber().then((num)=>{
return [num, counter];
}).then(res){
If(counter == res[1])
el.innerHTML = res[0];
}, 10000);
I need to write a function that gets the auth number every 10s & displays it in the block below. I’ve tried using set interval but getAuthNumber() can take more than 10s to return in which case, I need to discard that response and only show the current value.
Do not use setInterval instead use setTimeOut function.
setInterval execution depends on the CPU usage, if it in case increased the setinterval will not gets completed in the interval specified
What you can do is run an async function inside setInterval. In the following code snippet, the getAuth function completes after 2s but setInterval runs every 1s. But it still works because there is an async funciton inside setInterval.
const getAuth = () => {
return new Promise((res, rej) => {
setTimeout(() => res(Math.random()), 2000);
});
};
const setDiv = async () => {
const res = await getAuth();
console.log(res);
};
setInterval(setDiv, 1000);
I have adapted this gist by Jake Archibald (see JavaScript counters the hard way - HTTP 203) into the following code:
function promiseInterval(milliseconds, signal, promiseFactory, callback) {
const start = performance.now();
function tick(time) {
if (signal.aborted){
return;
}
promiseFactory().then(
value => {
callback(value);
scheduleTick(time);
}
);
}
function scheduleTick(time) {
const elapsed = time - start;
const roundedElapsed = Math.round(elapsed / milliseconds) * milliseconds;
const targetNext = start + roundedElapsed + milliseconds;
const delay = targetNext - performance.now();
setTimeout(tick, delay);
}
scheduleTick(start);
}
Starting from the gist, I have removed the use of requestAnimationFrame and document.timeline.currentTime (using only performance.now), and I have added the promiseFactory parameter, plus some renaming (animationInterval renamed to promiseInterval, ms renamed to milliseconds and scheduleFrame renamed to scheduleTick) and formatting.
You would use it like this:
const controller = new AbortController(); // This is used to stop
promiseInterval(
10000, // 10s
controller.signal, // signal, to stop the process call `controller.abort`
getAuthNumber, // the promise factory
num => {el.innerHTML = num;} // this is what you do with the values
);
It will not really call getAuthNumber each 10 seconds. Instead, it will wait until getAuthNumber completes and schedule to call on the next 10 seconds interval, and repeat. So it is not calling it multiple times and discarding values.
I'm fetching some data from firebase and would like to run async/await function (to fetch data) only once upon the first page load. I'm used to React and lifecycle methods / hooks doing it but this little project is just too small to use React. I just need to run this function once, fetch the data, save it to a variable and do not make any further calls to firebase api in the same session.
async function getEntries() {
const snapshot = await firebase.firestore().collection('riders').get()
// Do my thing with the data, etc.
// console.log(snapshot.docs.map(doc => doc.data()));
}
Is there any js-only way of running this function only once when the page loads?
If you call a function just once, why do you need the function at all?
const snapshot = await firebase.firestore().collection('riders').get()
// Do my thing with the data, etc.
// console.log(snapshot.docs.map(doc => doc.data()));
This top level await only works in modules, and it blocks all depending modules to load. If that is not necessary (they don't depend on the data), or if you don't want write a module, you can wrap the code in an async IIFE, and store the returned promise in a variable:
const dataPromise = (async function() {
//...
return data;
})();
While the data is loading, you might want to show some loading icon or so. That can easily be done with the following hook:
function usePromise(p) {
const [state, setState] = useState(null);
useEffect(() => { p.then(setState); }, []);
return state;
}
// Inside a component:
const data = usePromise(dataPromise);
if(data === null)
return <Loading />;
// show data
Yes. You can use Self Invoking (self executing) Functions. Syntax is like:
(function(){})();
The last parentheses are for running function. the function is anonymous.
You can Implement it this way:
(async function () {
const snapshot = await firebase.firestore().collection('riders').get()
})();
in this way you can never call this function again and it will run only once.
Tutorial: https://blog.mgechev.com/2012/08/29/self-invoking-functions-in-javascript-or-immediately-invoked-function-expression/
And The question you asked is somehow duplicate and answered here: Function in JavaScript that can be called only once
What you are looking for is memoization of the function result. There are several libraries to supporting including react.
Theres also a handmade pattern you can use by changing the function implementation after it's called once, accoring to JavaScript: The Good Parts
async function getEntries() {
const snapshot = await firebase.firestore().collection('riders').get()
// Do my thing with the data, etc.
// console.log(snapshot.docs.map(doc => doc.data()));
getEntries = async function(){
return snapshot
}
return snapshot
}
I think you can load it with the load method when the page is first loaded and then set it to cookie or local stroge. You can check this value on next page loads. You can do this quickly using jQuery.
$(window).load(function() {
var item = localStorage.getItem('test');
if(item != null){
// your code
}
else {
localStorage.setItem('test', 1);
}
});
The simplest way is to make a global variable like:
let isCalled = false;
and in the function body do:
if(isCalled) return;
//the stuff the function would do
isCalled = true;
//Assign isCalled to true before using a return statement as it will make the program discard the lines below it.
I have an Angular 7 application and I am using RxJS.
I want to fetch a list of stuff from an endpoint every 30 seconds, but sometimes the request can take a while and I want to account for that time.
Example: if the request takes 10 seconds, then I want to call it again after 40 seconds (30 + 10), not 20 (30 - 10).
I am trying with:
fetchList() {
this.service.fetchListFromHttp()
.pipe(
finalize(() =>
setTimeout(() => {
this.fetchList();
}, 30000)
)
)
.subscribe(
result => this.list = result,
err => this.logError(err)
);
}
I would imagine that when the http requests, it would trigger another call to fetchInfo after 30 seconds, but what happens is that I get weird intervals. For instance, the funcion gets called every 10 seconds, or 20 seconds, and weird intervals like that.
I would assume that the interval would always be bigger then 30 seconds.
Are you connecting to your own backend? If you're the one who has coded the API to fetch information form, socket.io is probably what you're looking for to accomplish this.
Have you looked into using RxJS? it comes with Angular, I'm sure what you could accomplish something that fetches then pauses
something similar to this might work
this.service.fetchInfoFromHttp().pipe(delay(30 * 60 * 10000), () => ...), repeat(Infinity))
Here is the link to the documentation on RxJS
RxJS delay
RxJS repeat
Socket.io
EDIT:
How Can I make a Timer using an observable in Angular 9
EDIT AGAIN:
Thanks for your comment #goga-koreli, maybe debounce will work
https://www.learnrxjs.io/learn-rxjs/operators/filtering/debounce
Improving upon SVNTY's answer, you can do it like so:
fetchInfo() {
const passAndWait = (data) => interval(30000).pipe(
take(1),
ignoreElements(), // this is needed to discard interval numbers
startWith(data),
)
// get data from network pass it to subscriber and wait for specified time
// repeat will start everything again when interval completes with take(1)
this.service.fetchInfoFromHttp().pipe(
concatMap((data) => passAndWait(data)),
repeat(Infinity),
).subscribe();
}
use observable from rxjs https://stackblitz.com/edit/angular-q714uq
import { interval, Subject, PartialObserver, Observable } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
after import libraries set interval to 30 then start after finish call your service when service get result again start interval
ispause = new Subject();
private time = 30;
timer: Observable<number>;
timerObserver: PartialObserver<number>;
ngOnInit(){
this.timer = interval(1000)
.pipe(
takeUntil(this.ispause)
);
this.timerObserver = {
next: (_: number) => {
if(this.time==0){
this.ispause.next;
this.callservice();
}
this.time -= 1;
}
};
this.timer.subscribe(this.timerObserver);
}
callservice(){
//after u get data
this.service.fetchListFromHttp()
.pipe(
finalize(() =>
this.time=30;
this.timer.subscribe(this.timerObserver);
)
)
.subscribe(
result => this.list = result,
err => this.logError(err)
);
}
I have this function. Basically it searches data from database and does something with it. In this demo it just increases counter.
exports.fullThreads = function(){
return new Promise((resolve, reject) => {
MongoClient.connect(mongoUrl, (err, db) => {
var fullThreads = db.collection('tmp_fullThreads'),
threadPages = db.collection('tmp_threadPages').find();
var counter = 0;
threadPages.on('data', (doc) => {
setTimeout(function(){
counter++;
}, 200)
});
threadPages.on('end', () => {
console.log('end');
console.log(counter);
});
});//connect
});//promise
}//fullthreads
In this example I expected that it would call for data, wait 200ms, and then increase counter. Once it reaches the end, it would end. However it is different. on('end') is called before on('data') actually finished. I guess that is because it didn't return value immediately. So this setTimeout function is not a way to go. However I have a real problem here. This is simply way too fast and in real life code, this wouldn't increase counter, it would call foreign API which doesn't accept so many calls in so little time, so I wanted to make a delay between each call, how is this done properly?
Then you would need some kind of queu
eg:
queuTasks = [];
queuTasks.push("Task1");
queuTasks.push("Task2");
function doTasks(){
// Do your task
if(queuTasks.length > 0){
console.log(queuTasks[0]);
queuTasks.splice(0,1); // Remove it from the queu
}
setTimeout(function(){ doTasks();}, 1000);
}
This is just some quick code i made, may not work out of the box. But think you get the idea.
I know this is not entirely what you were asking about
This is my workaround:
var threadPages = db.collection('tmp_threadPages').find();
var delay = 0;
function test(doc, delay){
setTimeout(function(){
console.log(delay);
}, delay)
}
threadPages.on('data', (doc) => {
test(doc, delay);
delay += 100;
});
It works well if you do not need to bind proper events to on('end method. Basically it increases delay per request, otherwise it would fire all of them at once. This makes code go slower and it wouldn't overkill API with too many requests per second.