Consider the following setup, regarding asynchronous functions:
Client.prototype.auth = function(callback) {
//authenticate the client
//run callback
};
Client.prototype.get = function() {
this.auth(function(){
//run the rest of this `get` function
}
};
The get function is called numerous times through an eventlistener, and this event fires only once
The very first get should start the authentication which stays valid for every subsequent calls
The authentication function is takes a couple of seconds to complete
Every subsequent get call does not need to reauthenticate because it is still valid because of the first function call
Every subsequent get call should only be run after the client is authenticated. If it is not authenticated it should wait for the authentication to finish
The point is to prevent 10 get calls to fire 10 auth calls. Whenever the 1st auth function gets called, the other 9 get calls should wait for it to finish and then carry on with the rest of the get function (while being authenticated)
I cant get my head around this. I tried to keep this example as simple as possible
I think solution for you is caching. Make a cache that will hold value isUserAutheniticated and isAuthenitcationProcess and when you need to call auth just check if user is authenticated and if not call it. Inside auth subscribe callback, check if authentication process is open if not do authentication set and call all registered callbacks. Globallist is not cleanest option to implement Observable pattern so you can do it in other way
Here is my idea:
var isAuthenticated = false;
var isAuthenticatioProcess = false;
var authenticationCallbacks = [];
Client.prototype.auth = function(callback) {
authenitcationCallbacks.push(callback);
if (isAuthenticonProcess) {
return;
}
//authenticate
authenitcationCallbacks.forEach(function(call) {
call();
});
authenitcationCallbacks = [];
isAuthenticonProcess = false;
isAuthenticated = true;
};
Client.prototype.get = function() {
if (isAuthenticated) {
this.auth(function(){
//run the rest of this `get` function
}
} else {
function(){
//run the rest of this `get` function
}
}
};
If you could use Async.js look at this answer
Related
I'm seriously stuck here, i'm setting up ApplePay that requires a user initiated event to create an AppleSession, but i need to do a series of API calls before that happens, unfortunately due to business rules they can only happen once the user hits checkout.
So the flow is checkout method gets called, in that method we do an await as the process action gets called, then provided it is successful we initialize ApplePay. Looks something like this.
checkout( event ) {
await this.processPayments();
this.checkoutAP();
}
This fails with an error from ApplePay that it needs a user initiated event to create an ApplePaySession. The await returns a promise and wraps any code following it in it's .then. So the call to checkoutAP no longer has a user initiated event in it's stack/context. So it fails.
I tried using a setInterval and check the state value, it will be updated once the process call is done. The code doesn't wait for a setInterval unless you wrap it in a promise, which brings me back to the same issue.
This is my current hacky mess that doesn't work.
await this.processAmenityPayments(this.getPaymentList()).then(( result ) => {
this.paymentsProcessStatus = PAYMENTS_PROCESS_STATUS_COMPLETE;
}, ( error ) => {
this.paymentsProcessStatus = PAYMENTS_PROCESS_STATUS_ERROR;
this.errors.push(error);
});
let intervalCount = 0;
const interval = setInterval(()=> {
if( this.paymentsProcessStatus === PAYMENTS_PROCESS_STATUS_COMPLETE || this.paymentsProcessStatus === PAYMENTS_PROCESS_STATUS_ERROR) {
clearInterval(interval);
// I can't put it here, it's a different context and fails.
}
intervalCount++;
if( intervalCount > 20 ) {
clearInterval(interval);
return null;
}
}, 1000 );
// my hope is i can wait for the mess to run above, once the state has been updated then
// allow the code to continue at this point.
this.continueCheckout();
Any ideas, no matter how hacky would be most welcome.
Thanks Guys!
I have two APIs, the first is start setInterval and second is clearInterval. Of course the second API doesn't work, but how to make it work? As I understand there is no global variable where I can assign setInterval. Any ideas?
let glob = 0;
let timer = null;
exports.start = functions.https.onRequest((req, res) => {
if(timer){
}else {
queueFunction();
}
res.status(status).send(mes);
});
});
exports.stop= functions.https.onRequest((req, res) => {
if(timer){
clearInterval(timer);
}else {
}
res.status(status).send(mes);
});
});
function queueFunction() {
timer = setInterval(timerFunc, 3000);
}
function timerFunc(){
glob++;
}
What you're trying to do is not possible. Each function runs in a completely isolated server instance. They know nothing about each other, and have no shared state.
Furthermore, after an HTTP function sends a response, it is effectively terminated and cleaned up. You cannot have ongoing work continue in that server instance after a function is terminated.
If you need to persist some state in your function, you should use a storage mechanism such as Realtime Database or Firestore. If you need work to appear to continue after a function terminates, you will have to spin up another function, typically via pub/sub. Or you can delegate to another server instance such as App Engine.
Sometime ago I had a code question in a take home test. It was as follows:
Database Throttling
You are given an array userInfo of user data and a function updateDB that takes a single user data argument. updateDB makes an asynchronous call that parses the user data and inserts the parsed data into a database. The database throttles requests so to make sure all user data is added to the database we need a function addAllUserData that calls updateDB on each entry in userInfo making sure never to exceed 7 calls per second to prevent being throttled.
var userInfo = [{'name':'antonio', 'username':'antonio_pavicevac_ortiz'}], dataBase = [];
function updateDB(singleUserDataArgument, callback){
dataBase.push(callback(singleUserDataArgument));
}
function addAllUserInfo(data) {
var eachUserData;
setInterval(function(){
eachUserData = data.map(data)
}, 7000);
}
As you can see by my attempt I am having a hard time wrapping my head around this exercise. Could anyone also inject what is meant by throttling in regards to async calls?
Thanks in advance!
// contains times at which requests were made
var callTimes = [];
function doThrottle(){
// get the current time
var time - new Date().getTime();
// filter callTimes to only include requests this second
callTimes = callTimes.filter(function(t){
return t > time-1000;
});
// if there were more than 7 calls this second, do not make another one
if(callTimes.length > 7) return true;
else{
// safe, do not throttle
callTimes.push(time);
return false;
}
}
// use like this
function makeRequest(){
if(doThrottle()){ /* too many requests, throttle */ }
else{ /* it's safe, make the ajax call*/ }
}
I am trying to fake synchronous JavaScript while doing an AJAX request.
I have an getPagePath(id) function that needs to get the page path of an page by giving it an page ID, it receives the data trough an web API. I thought this was going to be simple, just do an ajax request to the server and receive the page path. But what is happening: when requesting the page path my code keeps running and returns an empty var, after that the ajax call finishes, but to late.
I know my explaining is not much saying so here is my code:
var getPagePath = function() {
// Function to check if this.pagePath is set.
var pagePathReady = function() {
console.log('PAGEPATH: CHECKING');
if (this.pagePath && this.pagePath != null) {
return true;
} else {
return false;
}
};
if (!pagePathReady()) {
// No pagePath defined so lets set it.
this._setPagePath();
while (!pagePathReady())
{
// Not yet defined, check again..
// *** The problem ***
// This while loop is running insanely fast making the browser crash.
// How can I make this wile loop pause for 1 sec?
// *******************
console.log('PAGEPATH: NOT READY -> CHECK AGAIN');
}
// READY
console.log('PAGEPATH: READY -> VALUE: ' + this.pagePath);
return this.pagePath;
} else {
return this.pagePath;
}
};
var _setPagePath = function() {
if (!this.pagePathRequestFired) {
this.pagePathRequestFired = true;
// Fire request.
system.url(
this.getNodeId(),
function(url) {
// Request ready, set pagePath.
this.pagePath = url;
this.pagePathRequestFired = false;
},
this
);
} else {
// Call already running..
}
};
I have set the problem in the more explaining comments.
Thanks in advance!
You can make an ajax call synchronous if you really need to.
xmlhttp.open("GET", "url", false);
Note the 3rd param.
But, I think you just need more practice in writing your code to work with the event/callback concept.
Instead of polling the pagePath (which doesn't seem to be required IMHO) why not just execute a callback when the _setPagePath is ready? If you want to fake a synchronous request, you can just display a loading spinner to the user as an overlay, disabling the UI.
I have a simple Javascript function:
makeRequest();
It does a bunch of stuff and places a bunch of content into the DOM.
I make a few calls like so:
makeRequest('food');
makeRequest('shopping');
However, they both fire so quickly that they are stepping on each other's toes. Ultimately I need it to have the functionality of.
makeRequest('food');
wait....
makeRequest('shopping'); only if makeRequest('food') has finished
Thoughts on getting these to execute only one at a time?
Thanks!
If these functions actually do an AJAX request, you are better keeping them asynchronous. You can make a synchronous AJAX request but it will stop the browser from responding and lead to bad user experience.
If what you require if that these AJAX requests are made one after the other because they depend on each other, you should investigate your function to see if it provides a callback mechanism.
makeRequest('food', function()
{
// called when food request is done
makeRequest('shopping');
});
Using jQuery, it looks something like that
$.get("/food", function(food)
{
// do something with food
$.get("/shopping", function(shopping)
{
// do something with shopping
});
});
I would recommend that you simply write them asynchronously--for example, call makeRequest('shopping'); from the AJAX completion handler of the first call.
If you do not want to write your code asynchronously, see Javascript Strands
I suppose that you have a callback method that takes care of the response for the request? Once it has done that, let it make the next request.
Declare an array for the queue, and a flag to keep track of the status:
var queue = [], requestRunning = false;
In the makeRequest method:
if (requestRunning) {
queue.push(requestParameter);
} else {
requestRunning = true;
// do the request
}
In the callback method, after taking care of the response:
if (queue.length > 0) {
var requestParameter = queue.splice(0,1)[0];
// do the request
} else {
requestRunning = false;
}