I'm interacting with a third-party JavaScript library where some function calls are asynchronous. Instead of working the asynchronous logic into my application, I preferred to write synchronous wrappers to those async calls. I know, I know, it's terrible design, but this is a demo project with very high chance of being rewritten entirely. I need something to show the team the concept, not really having to worry performance, yet.
Here's what I wanna do:
function sync_call(input) {
var value;
// Assume the async call always succeed
async_call(input, function(result) {value = result;} );
return value;
}
I tried the jQuery's deferred and promise but it seems to be aiming at the async design pattern. I want to use the synchronous pattern in my code.
This will never work, because the JS VM has moved on from that async_call and returned the value, which you haven't set yet.
Don't try to fight what is natural and built-in the language behaviour. You should use a callback technique or a promise.
function f(input, callback) {
var value;
// Assume the async call always succeed
async_call(input, function(result) { callback(result) };
}
The other option is to use a promise, have a look at Q. This way you return a promise, and then you attach a then listener to it, which is basically the same as a callback. When the promise resolves, the then will trigger.
How about calling a function from within your callback instead of returning a value in sync_call()?
function sync_call(input) {
var value;
// Assume the async call always succeed
async_call(input, function(result) {
value = result;
use_value(value);
} );
}
Here is a working example of how:-
function testAsync(){
return new Promise((resolve,reject)=>{
//here our function should be implemented
setTimeout(()=>{
console.log("Hello from inside the testAsync function");
resolve();
;} , 5000
);
});
}
async function callerFun(){
console.log("Caller");
await testAsync();
console.log("After waiting");
}
callerFun();
Outputs:
Caller
Hello from inside the testAsync function
After waiting
To make it more complete, error handling should be added (deal with the reject() case).
See here for other examples: https://www.delftstack.com/howto/javascript/javascript-wait-for-function-to-finish/
Related
I use jQuery. And I don't want parallel AJAX calls on my application, each call must wait the previous before starting. How to implement it? There is any helper?
UPDATE If there is any synchronous version of the XMLHttpRequest or jQuery.post I would like to know. But sequential != synchronous, and I would like an asynchronous and sequential solution.
There's a much better way to do this than using synchronous ajax calls. Jquery ajax returns a deferred so you can just use pipe chaining to make sure that each ajax call finishes before the next runs. Here's a working example with a more in depth example you can play with on jsfiddle.
// How to force async functions to execute sequentially
// by using deferred pipe chaining.
// The master deferred.
var dfd = $.Deferred(), // Master deferred
dfdNext = dfd; // Next deferred in the chain
x = 0, // Loop index
values = [],
// Simulates $.ajax, but with predictable behaviour.
// You only need to understand that higher 'value' param
// will finish earlier.
simulateAjax = function (value) {
var dfdAjax = $.Deferred();
setTimeout(
function () {
dfdAjax.resolve(value);
},
1000 - (value * 100)
);
return dfdAjax.promise();
},
// This would be a user function that makes an ajax request.
// In normal code you'd be using $.ajax instead of simulateAjax.
requestAjax = function (value) {
return simulateAjax(value);
};
// Start the pipe chain. You should be able to do
// this anywhere in the program, even
// at the end,and it should still give the same results.
dfd.resolve();
// Deferred pipe chaining.
// What you want to note here is that an new
// ajax call will not start until the previous
// ajax call is completely finished.
for (x = 1; x <= 4; x++) {
values.push(x);
dfdNext = dfdNext.pipe(function () {
var value = values.shift();
return requestAjax(value).
done(function(response) {
// Process the response here.
});
});
}
Some people have commented they have no clue what the code does. In order to understand it, you first need to understand javascript promises. I am pretty sure promises are soon to be a native javascript language feature, so that should give you a good incentive to learn.
You have two choices that I can think of. One is to chain them through callbacks. The other is to make the calls synchronous rather than async.
Is there a reason you want them sequential? That will slow things down.
To make the call synchronous, you'll set the async option in the Ajax call to false. See the documentation at http://docs.jquery.com/Ajax/jQuery.ajax#options (click options tab to see them).
(async () => {
for(f of ['1.json','2.json','3.json']){
var json = await $.getJSON(f);
console.log(json)
};
})()
requests 3 json files with jQuery ajax calls
process in sequence (not in parallel) with await
works in Chrome/Firefox/Edge (as of 1/30/2018)
more at MDN
The best way you could do this is by chaining callbacks as Nosredna said. I wouldn't recommend using synchronous XMLHttpRequest as they lock your entire application.
There aren't much helper for this as far as I know, but you could do something resembling a callback FIFO.
You could give narrative javascript a try http://www.neilmix.com/narrativejs/doc/
I've never used it myself though. If I wanted to do this, I would setup some kind of abstraction for chaining asynchronous actions. As others have said, the synchonous version of the ajax object blocks events from being processed while it's waiting for a response. This causes the browser to look like it's frozen until it recieves a response.
Set the async option to false, e.g.,
$.ajax({ async: false /*, your_other_ajax_options_here */ });
Reference: Ajax/jQuery.ajax
You can use promise to make ajax calls sequential. Using Array push and pop promise method, sequential ajax calls will be lot easier.
var promises = [Promise.resolve()];
function methodThatReturnsAPromise(id) {
return new Promise((resolve, reject) => {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/todos/'+id,
dataType:'json',
success: function(data)
{
console.log("Ajax Request Id"+id);
console.log(data);
resolve();
}
});
});
}
function pushPromise(id)
{
promises.push(promises.pop().then(function(){
return methodThatReturnsAPromise(id)}));
}
pushPromise(1);
pushPromise(3);
pushPromise(2);
Look at this: http://docs.jquery.com/Ajax/jQuery.ajax (click on the "options" tab).
But remember a synchronous call will freeze the page until the response is received, so it can't be used in a production site, because users will get mad if for any reason they have to wait 30 seconds with their browser frozen.
EDIT: ok, with your update it's clearer what you want to achieve ;)
So, your code may look like this:
$.getJSON("http://example.com/jsoncall", function(data) {
process(data);
$.getJSON("http://example.com/jsoncall2", function (data) {
processAgain(data);
$.getJSON("http://example.com/anotherjsoncall", function(data) {
processAgainAndAgain(data);
});
});
});
This way, the second call will only be issued when the response to the first call has been received and processed, and the third call will only be issued when the response to the second call has been received and processed. This code is for getJSON but it can be adapted to $.ajax.
The modern way of sequencing jQuery asynchronous operations is to use the promises they already return and the flow control that promises support and this is not currently shown in any of the other answers here from prior years.
For example, let's suppose you wanted to load several scripts with $.getScript(), but the scripts must be loaded sequentially so the second one doesn't load/run until the first has finished and so on and you want to know when they are all done. You can directly use the promise that $.getScript() already returns. For simplicity, you can await that promise in a for loop like this:
async function loadScripts(scriptsToLoad) {
for (const src of scriptsToLoad) {
await $.getScript(src);
}
}
loadScripts([url1, url2, url3]).then(() => {
console.log("all done loading scripts");
}).catch(err => {
console.log(err);
});
Since all jQuery Ajax-related asynchronous operations now return promises (and have for many years now), you can extend this concept to any of jQuery's Ajax-related operations.
Also, note that all the other attempts in other answers here to wrap a jQuery operation in a new promise or in a jQuery deferred are obsolete and considered a promise anti-pattern because when the operation itself already returns a promise, you can just use that promise directly without trying to wrap it in your own new promise.
Synchronous calls aren't necessarily slower, if you have an app where AJAX calls open, posts to, then closes a socket, multiple calls to the socket don't make sense as some sockets can only handle a single connection, in which case, queuing data so its only sent when the previous AJAX call has completed means much higher data throughput.
How about using Node.js events?
var EventEmitter = require('events').EventEmitter;
var eventEmitter = new EventEmitter();
var $ = require('jquery');
var doSomething = function (responseData) {
var nextRequestData = {};
// do something with responseData
return nextRequestData;
};
// ajax requests
var request1 = $.ajax;
var request2 = $.ajax;
var requests = [request1, request2];
eventEmitter.on('next', function (i, requestData) {
requests[i](requestData).then(
function (responseData) {
console.log(i, 'request completed');
if (i+1 < requests.length) {
var nextRequestData = doSomething(responseData);
eventEmitter.emit('next', i+1, nextRequestData);
}
else {
console.log('completed all requests');
}
},
function () {
console.log(i, 'request failed');
}
);
});
var data = {
//data to send with request 1
};
eventEmitter.emit('next', 0, data);
sequential != synchronous, and I would like an asynchronous and sequential solution
Synchronous execution generally means "using the same clock", while sequential execution means "following in order or sequence".
For your specific use case I think both conditions must be met, as asynchronous execution implies the possibility of a non-sequential result.
I consider rewriting existing callback-based code into code using promises. However I'm unsure if this makes sense and how to start. The following code-snippet is a mostly self-contained example from that code:
function addTooltip($element, serverEndpoint, data) {
'use strict';
const DELAY = 300;
const TOOLTIP_PARENT_CLASS = 'hasTooltip';
let timeOutReference;
$element.hover(function hoverStart() {
if ($element.hasClass(TOOLTIP_PARENT_CLASS)) {
return;
}
timeOutReference = setTimeout(function getToolTip() {
const $span = jQuery('<span class="serverToolTip">');
$span.html(jQuery('<span class="waiting">'));
$element.append($span);
$element.addClass(TOOLTIP_PARENT_CLASS);
jQuery.get(serverEndpoint, data).done(function injectTooltip(response) {
$span.html(response.data);
}).fail(handleFailedAjax);
}, DELAY);
}, function hoverEnd() {
clearTimeout(timeOutReference);
});
};
Intended functionality: When the user hovers over $element for 300ms the tooltip-content is requested from the server and appended to $element.
Does it make sense to rewrite that code with promises and how would I do it?
(jQuery is provided by the framework (dokuwiki), so we might as well use it.)
Prior research:
https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
other SO questions about this topic which left me unsure about whether this is a sensible idea and how to do it
First, you'd need to wrap setTimeout into a promise. Simply create a function that accepts a timeout and returns a promise that resolves after that timeout.
Next, since jQuery.get already returns a promise, you just need to put it inside the promise resolve handler and return its promise. That way the next chained then listens to that promise instead of the timer's.
It would look something like:
function timer(n){
return Promise(function(resolve){
setTimeout(resolve, n);
});
}
timer(DELAY).then(function(){
return jQuery.get(...)
}).then(function(response){
// jQuery.get promise resolved
}).catch(function(error){
// something failed somewhere
});
As for your question
Does it make sense to rewrite that code with promises and how would I do it?
That depends on you. I find promise-based code more readable but takes time to write properly especially if if you intend to write pure callbacks and deal with multiple async operations. I usually write my code callbacks-first if the API is simpler to write that way and refactor later for readability.
To elaborate on my comment above. Below is an example of how promises can make dependent-callback code (arguably) more readable (Basically, it destroys the nesting of callbacks-in-callbacks):
Again, in the case of the code snippet you posted, I hardly see how it's worth it (unless your doing it as an exercise).
With Callbacks
function someAsyncMethod(callback) {
$.get({...})
.then(callback);
}
function anotherAsyncMethod(callback) {
$.get({...})
.then(callback);
}
someAsyncMethod(function() {
anotherAsyncMethod(function yourFunction() {
//do something
});
});
With Promises:
function someAsyncMethod() {
return $.get({...});
}
function anotherAsycnMethod() {
return $.get({...});
}
someAsyncMethod()
.then(anotherAsyncMethod)
.then(function yourFunction() {
//do something
})
I have a validation library that is purely synchronous but is often used as part of a chain of async functions. However, I have to maintain an existing synchronous API and would like to make the promise API optional.
Can I somehow detect (at runtime) whether a function is part of a Promise chain?
This is pretty easy with callbacks since you can just check if a callback was passed in. I understand that I could pass in an optional promise boolean, but that seems inelegant.
I've also considered doing a callback interface and using a library to convert the callback interface to a promise based interface on the fly. However, I'm working in Haxe and I would prefer to keep the transforms/abstractions down to a minimum.
I also understand that you can sandwich regular functions in-between promises, but there are some instances where the behavior would differ between the two.
FINAL Edit Lots of confusion as to why I can't just return the same values, first example (below) didn't seem to help. Remember, this is still simplified.
//mix of sync with promise
new Promise(function(resolve, reject){
var safeToAdd = thingTracker.preflight(newThing);
if(safeToAdd){
return client.request.addThing(newThing); //send request to server
} else {
reject(newThing.errorMessages); //requires explicit reject, cannot just pass results along
}
}).then(function(newThing){ //client and server both cool with newThing?
thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
ui.show(errorMessages);
});
//pure promise
thingTracker.preflight(newThing).then(function(){
return client.request.addThing(newThing); //sends request to server
}).then(function(newThing){ //client and server both cool with newThing?
thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
ui.show(errorMessages);
});
(Old) Edit to clarify (but didn't really):
function preflight(thing){
var validity = thing === 42;
if(promise){
if(validity){
return Promise.resolve(validity);
} else {
return Promise.reject(validity);
}
} else {
return validity;
}
}
Obviously I can do the same checks in a then's anon function, but that is not much better than using the sync interface directly. Also note that this is a very simple example, in the real function there are side effects on thing and messages are produced.
Edit Just to illustrate my point a bit better, here is a gist of what things look like.
Synchronous functions can be used within a promise chain just fine. If you have a synchronous API call that is called like this:
var info = myApi("foo");
That can be used just fine within a promise chain:
someAsyncCall().then(myApi).then(someOtherFunction)
You don't need to make myApi async or return a promise to be used this way. The only thing you can't do in a promise chain with a synchronous function is that it can't be the first item in the chain, but then it doesn't need to be. Since it's synchronous, it can just be called before you start the chain if anyone wants it to execute first. Or, worst case, you can do:
Promise.resolve().then(myAPI).then(someOtherFunction);
Can I somehow detect (at runtime) whether a function is part of a
Promise chain?
No, you cannot (unless you explicitly pass it some info that tells it that) and you do not need to in order to use it in a promise chain. You do not need to return a promise to be a .then() handler. You can just return a value and that value will become the value of the promise chain at that point in the chain.
I also understand that you can sandwich regular functions in-between
promises, but there are some instances where the behavior would differ
between the two ... such as returning false vs throwing a message.
It is not clear to me why the behavior would have to be different. If returning false is your normal synchronous behavior, you can do that just fine in a promise chain - the next step in the chain just needs to handle the false value that is passed to it, just like the next line of synchronous code would do. If throwing an exception is your normal behavior, you can do that just fine in a promise chain too (the promise chain will turn the exception into a rejection) which the following code can decide how it wants to handle. One can make a good argument that it would be worse if your synchronous function behaved differently based on whether it was part of a promise chain or not. If you see a good reason for two different behaviors, then you should probably have either two different functions (that can be documented differently) or an option passed in that determines the behavior.
Comment based on the code you added in your edit
You seem to think that when called via a promise chain that you need to return a resolved or rejected promise chain. You do not need to do that. You can just return a normal value and the promise chain will inherit the value you return. There is really no reason to return a resolved or rejected promise unless you want it to be the first function in a promise chain, but in that case, it isn't in a promise chain yet anyway so you could never detect that. And, there's no reason for a synchronous operation to be the first one in a promise chain. Just call the synchronous operation first and then initiation your promise chain with the async operations.
Here's a demo of a synchronous function being used in a promise chain:
function log(str) {
var div = document.createElement("div");
div.innerHTML = str;
document.body.appendChild(div);
}
// test async call
function delay(t, val) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(val);
}, t);
});
}
function square(x) {
return x * x;
}
log("Calculating the square of 9");
delay(500, 9).then(square).then(function(result) {
log("Result = " + result);
});
Or, you can even do this to initiate a promise chain with your synchronous operation as the first operation in the chain:
function log(str) {
var div = document.createElement("div");
div.innerHTML = str;
document.body.appendChild(div);
}
function square(x) {
return x * x;
}
log("Calculating the square of 9");
Promise.resolve(9).then(square).then(function(result) {
log("Result = " + result);
});
I believe promises have only one argument so it's not possible to pass an isPromise boolean through the chain, even if you could, you'd have to consciously add the parameter, so why not just call the async function...
function preflight(thing){
return thing === 42;
}
function preflightAsync(thing){
return new Promise(resolver);
function resolver(resolve,reject){
setTimeout(function foo(){
preflight(thing) ? resolve(true,"this won't make it") : reject();
},1000);
}
}
// Promised
preflightAsync(42).then(doSomethingWithIt).catch(doSomethingWithIt);
preflightAsync(89).then(doSomethingWithIt).catch(doSomethingWithIt);
// Call Directly
doSomethingWithIt(preflight(42));
doSomethingWithIt(preflight(89));
function doSomethingWithIt(result,nada){
result = result ? "BAM!!!" : "WAM!!!";
console.log("doSomethingWithIt",result,nada);//doSomethingWithIt BAM!!! undefined
return result;
}
These examples use the new native Promise function, browser support is limited.
As we can see from previous answers you can't use exactly the same function also for Promises. Adding my workaround here which is unfortunately extending the function prototype ...
The problem is that if you want to use the same function synchronously and in a Promise the use of arguments would differ.
As an example - a function truncating a string:
sync:
truncate('A very long string coming from a sync. operation', 10);
async:
Promise.resolve('A very long string coming from a network request')
.then( truncate(10) )
The async version would make the truncate Function think that 10 is the string and you would need to check typeof in every function or do ducktyping.
What would work:
Promise.resolve('A very long string coming from a network request')
.then( truncate.it(10) )
if you extended the function prototype before:
Function.prototype.it = (function() {
return function() {
var args = arguments;
return function(){
Array.prototype.push.apply(arguments, args);
return this.apply(null, arguments);
}.bind(this);
};
}());
function foo(a, b, c) {
console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);
};
// LOGIC :
foo(1, 2, 3);
// SAME AS
foo.it(2, 3)(1);
// OR
foo.it(3)(1, 2);
// OR pretty useful for .then()
Promise.resolve(1).then(foo.it(2, 3));
fiddle
I have the following code:
function doSomething() {
//xhr here
setTimeout(function() {
var value = 42;
}, 10);
return {
then: function(callback) {
callback(value);
}
};
}
doSomething().then(function(result) {
log("got a result", result);
});
And can't figure out how to access the value.
I need this to be promise-based solution in order to use in multiple places
JSFidle link
Update:
We are not using any libraries in that projects
There are a couple of problems there:
value is local to the function you're passing into setTimeout, because that's where you've declared it. You could fix this issue by declaring it in doSomething instead.
The bigger issue is that what you have there isn't a promise, it's just a function that returns an object when you call it that has a then method. Here's the order in which things happen:
You call doSomething
It sets a timer to set a value.
It creates an object with a then function.
It returns the object.
You call the then function immediately.
then tries to access value (which it can't because of the declaration issue, but would be a problem anyway).
Some time later, value is set by the callback when the timer fires.
To be a promise, the then function on object you return would have to store a reference to the callback passed into it, and call the callback later, when value has been set (e.g., the promise has been fulfilled).
Rather than implementing your own promises library, I'd suggest using one of the several that have already been written and debugged.
I'm just pointing out, that in order to "fix" your issue you need to return the then this way:
function doSomething() {
//xhr here
return {
then: function(callback) {
setTimeout(function() {
callback(42); // callback from within the async action
}, 10);
}
};
}
doSomething().then(function(result) {
log("got a result", result);
});
Please read TJ's answer and consider using a promise library - also consider reading this post that explains how promise resolution looks like.
Namely: Your then needs to in turn return a promise when called (rather than just set a timeout) for chaining, and the callback should be assimilated before waiting for it.
I was unsure how node.js was able to realize what functions where async and which were not and how to create a custom async function.
Say I wanted to create a custom asynchronous function. I would be surprised if just because I called my last argument to the async function callback or cb that it would just know its an async function:
function f(arg1, callback){
//do stuff with arg1
arg1.doStuff()
//call callback
callback(null, arg1.result());
}
I tried something like that and it did not work async. How do you tell node.js that f is actually async?
NOTE: this answer was written in 2014, before the existence of async function, and before Promises gaining popularity. While the same principles apply as well, I would recommend reading on Promises before trying to get your head around how they relate to "traditional" callback-driven async functions.
To create a function that calls its callback asynchronously, you have to use some platform-provided async primitive (typically IO-related) on it - timers, reading from the filesystem, making a request etc.
For example, this function takes a callback argument, and calls it 100ms after:
function asyncFn(callback) {
setTimeout(() => {
callback();
}, 100);
}
A possible reason for making a function async when it doesn't need to be, is for API consistency. For example, suppose you have a function that makes a network request, and caches the result for later calls:
var cache = null;
function makeRequest(callback) {
if (!cache) {
makeAjax(result => {
cache = result;
callback(result);
});
} else {
callback(cache);
}
}
The problem is, this function is inconsistent: sometimes it is asynchronous, sometimes it isn't. Suppose you have a consumer like this:
makeRequest(result => doSomethingWithResult(result));
doSomethingElse();
The doSomethingElse function may run before or after the doSomethingWithResult function, depending on whether the result was cached or not. Now, if you use an async primitive on the makeRequest function, such as process.nextTick:
var cache = null;
function makeRequest(callback) {
if(!cache) {
makeAjax(result => {
cache = result;
callback(result);
});
} else {
process.nextTick(() => callback(cache));
}
}
The call is always async, and doSomethingElse always runs before doSomethingWithResult.
Only native functions (with access to the event loop) are asynchronous. You would need to call one of them to get asynchronity for your callback. See What is a simple example of an asynchronous javascript function?.
If you aren't using any, there's hardly a reason to make your function asynchronous.