Am I nesting the function correctly? - javascript

I have little doubt, I have let say 5 or more js functions each calls C# functions Via Ajax calls doing various task like(fetching data from the database, validating it, calculations, saving it back to The database etc).
I am calling the function using Nested jQuery $.when(function1()).then(function2()) and so on. I want function one to fully complete before two and two before the third one and so one.... there are some dependency between these functions...
My Code example Like: (My Approach)
$(document).ready(function () {
$.when(one()).then(
$.when(two()).then(
$.when(three()).done(alert('three done')).then(
$.when(four()).then(
five()
).done(alert('All Finished Up'))
)
)
)
});
function one() //eg List 1000 records from Db
function two() //Update Some
function three() //validate Changes
function four() //save Changes
function five() // Some Other Task then
Now my question is simple I know rather than calling sequentially like
$(document).ready(function(){
one();
two();
three();
four();
five();
});
I used above nesting pattern (My Approach)... Is that good approach coding-wise and performance wise? Is there any more efficient approach than this?

Alternatively, you can run all of those calls in parallel to get a faster return
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function one(mode) {
await sleep(1000);
console.log(`${mode} : one() finished`);
}
async function two(mode) {
await sleep(3000);
console.log(`${mode} : two() finished`);
}
async function three(mode) {
await sleep(3000);
console.log(`${mode} : three() finished`);
}
async function four(mode) {
await sleep(4000);
console.log(`${mode} : four() finished`);
}
async function five(mode) {
await sleep(5000);
console.log(`${mode} : five() finished`);
}
async function runInSerial() {
await one('serial');
await two('serial');
await three('serial');
await four('serial');
await five('serial');
}
async function runInParallel() {
await Promise.all([
one('parallel'),
two('parallel'),
three('parallel'),
four('parallel'),
five('parallel'),
]);
}
$(document).ready(function () {
runInSerial().then(() => console.log('serial : all done!'));
runInParallel().then(() => console.log('parallel : all done!'));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

There is: if you want to only support browsers that support ES6, you can simply use the async/await syntax, as the jQuery Promises return a native Promise object anyway:
// Dummy async operation
function sleep(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
$(document).ready(function() {
run().then(() => console.log('all done'));
});
// Run all your async functions sequentially
async function run() {
await one();
console.log('one done');
await two();
console.log('two done');
}
async function one() {
return sleep(500);
}
async function two() {
return sleep(500);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Related

Refactor nested promises to a chain of promises

I have simplified this problem. I have 3 functions which I want to run with a 2 second delay between each. The following code is working:
$.when(one()).done(function () {
$.when(delay(2000)).done(function () {
$.when(two()).done(function () {
$.when(delay(2000)).done(function () {
$.when(three()).done(function () {
console.log('finished');
});
});
});
});
});
function delay(ms) {
var waitForDelay = new $.Deferred();
setTimeout(function () {
waitForDelay.resolve().promise();
}, ms);
return waitForDelay.promise();
}
function one() {
console.log('one');
return new $.Deferred().resolve().promise();
}
function two() {
console.log('two');
return new $.Deferred().resolve().promise();
}
function three() {
console.log('three');
return new $.Deferred().resolve().promise();
}
However when I try to refactor it, it no longer waits for the delayed amounts of time:
one().done(delay(2000)).done(two).done(delay(2000)).done(three).done(function () {
console.log('finished');
});
How can I refactor to chain these promises rather than nesting them? I want to use jQuery for older browser compatibility.
My advice would be to not use jQuery's weird promise-like data structures at all.
Have your functions return an actual Promise (either explicitly or by making them async functions) and await each step in the chain.
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
// an async function implicitly returns a promise
const one = async () => {
console.log("one");
}
// or you can return one manually
const two = () => {
console.log("two");
return Promise.resolve();
}
const three = async () => {
console.log("three");
}
// await each step in an async function (an async IIFE in this case)
(async () => {
await one();
await delay(2000);
await two();
await delay(2000);
await three();
console.log("finished");
})();
You can also use .then() if you prefer that style
// Convenience function so you don't repeat yourself
const waitTwoSeconds = () => delay(2000);
one()
.then(waitTwoSeconds)
.then(two)
.then(waitTwoSeconds)
.then(three)
.then(() => {
console.log("finished");
});
If you must use jQuery, it doesn't really look much different
function delay(ms) {
var d = $.Deferred();
setTimeout(d.resolve, ms);
return d.promise();
}
function one() {
console.log("one");
return $.Deferred().resolve().promise();
}
function two() {
console.log("two");
return $.Deferred().resolve().promise();
}
function three() {
console.log("three");
return $.Deferred().resolve().promise();
}
function waitTwoSeconds() {
return delay(2000);
}
one()
.then(waitTwoSeconds)
.then(two)
.then(waitTwoSeconds)
.then(three)
.then(function() {
console.log("finished");
});
<!-- Note jQuery 1.8 was the first to have chainable deferreds -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
I've used .then() instead of .done() because the latter does not chain and using $.when() is unnecessary when .then() is available.
Taking inspiration from Phil's answer, here is a solution compatible with older browsers.
First, change the delay() function to return a function:
function delay(ms) {
return function() {
var waitForDelay = new $.Deferred();
setTimeout(function () {
waitForDelay.resolve().promise();
}, ms);
return waitForDelay.promise();
}
}
All other functions stay the same as in the question.
Finally replace the .done()s with .then()s to make them chain:
one().then(delay(2000)).then(two).then(delay(2000)).then(three).then(function () {
console.log('finished');
});

await not doing what I expect

async function doThings() {
async function timer () {
setTimeout(() => {
console.log('timer!')
}), 1000
}
async function printer () {
console.log('printer!')
}
await timer()
await printer()
}
doThings()
I thought making the function async made it return a promise and await made it wait. Why does printer() finish first?
Your timer function doesn't work because setTimeout does not return a promise that could be awaited. You will need to promisify it manually:
// set a default of 1500
function timeout(ms=1500) {
return new Promise(resolve => setTimeout(resolve, ms));
};
async function printer() {
console.log('printer!');
};
async function doThings() {
await timeout(1000); // time in ms
await printer();
};
doThings();
The word “async” before a function means one simple thing: a function always returns a Promise. Other values are wrapped in a resolved Promise automatically.
Now when we use don't return Promise from async function, then javascript interpreter automatically returns resolved Promise, so that is the reason await on timer function got the resolved Promise and interpreter went ahead with the execution, but there is a setTimeout still going on. So we have to return a Promise from timer function, and resolve it inside setTimeout function like below.
async function doThings() {
async function timer() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`timer!`);
resolve();
}, 1000)
});
}
async function printer() {
console.log('printer!')
}
await timer()
await printer()
}
doThings()
All I can see is timer () and printer () functions are async functions but there is no await used in them, and this makes the async keyword unfunctional.

Make the function call synchronous

function first(){
console.log("1")
}
function second(){
new Promise ((resolve,reject)=>{
setTimeout(function(){
console.log("2")
resolve();
} ,0);
})
}
function third(){
console.log("3")
}
async function run(){
first();
await second();
third();
}
run();
Need to make the function call sync to get final output as 1,2,3 i tried creating the promise and use async await but that didnt help any other way
Pack the setTimeout into a promise and resolve in setTimeout,
Use async await for that promise in order for that to run consecutively
function first() {
console.log("1")
}
function second() {
return new Promise(res => {
setTimeout(function() {
console.log("2");
res()
}, 0)
})
}
function third() {
console.log("3")
}
async function run() {
first();
await second()
third();
}
run();

Javascript await a function inside an async function

I have the following codes:
function test1() {
test2.execute();
}
I am trying to use await inside an async function to make test1() execute first:
it("title", async () => {
await test1();
await firebase.assertSucceeds(true);
})
But somehow the function test1() does not execute before firebase.assertSucceeds(true). test1() returns a callback. I tried to use the callback, and it does not help either.
function test1() {
test2.execute().then(() => {
return;
});
}
it("title", async () => {
let x = await test1();
await firebase.assertSucceeds(true);
})
Still synchronous. what did I do wrong here?
In your first snippet you are awaiting for test1 which returns void (nothing), for the await to work it should be applyed to a promise, you acheive that either by returning test2.execute() which returns a Promise :
function test1() {
return test2.execute();
}
or by making test1 async and await for test2 to finish executing :
async function test1() {
await test2.execute();
}
ps: an async function returns a resolved promise with the return as the resolution value so for example :
async function test(){
return 2;
}
is translated under the hood to this:
function test(){
return Promise.resolve(2);
}
A function has to return a Promise if you want to await it.
For example:
function test1() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("I'm done")
}, 500)
})
}
Notice, in the firebase documentation, that assertSucceeds only takes a promise as it's one and only argument. So consider passing it a promise that always resolves if that's what you're going for to begin with.
await firebase.assertSucceeds(Promise.resolve())

I need to run an async function from a non-async function in order (sync) - is it possible?

Yes, it's a bad idea, a terrible idea.
I'm trying to make this:
async function delay(ms: number): Promise<void> {
await new Promise(r => setTimeout(r, ms));
console.log('called 3');
}
console.log('called 1');
(async () => {
console.log('called 2');
await delay(5000);
console.log('called 4');
})();
console.log('called 5');
Playground Link
Output this:
called 1
called 2
called 3
called 4
called 5
Instead of this:
called 1
called 2
called 5
called 3
called 4
Is this possible at all?
I took out the typescript tags and ran directly in the browser
altering the following lines
async function delay(ms: number): Promise<void> {
to direct JS
async function delay(ms) {
and it works as you would expect by awaiting in order ...
async function delay(ms) {
await new Promise(r => setTimeout(r, ms));
console.log('called 3');
}
console.log('called 1');
(async () => {
console.log('called 2');
await delay(5000);
await console.log('called 4');
console.log('called 5');
})();
It is important to remember that async functions are asynchronously so there would be no other way of calling console.log('called 5'); in order without placing it in the async function or using a then statement... I prefer the easier way of just putting the call in the async function..
It's not that is a "bad idea", is that you cannot use the syntax you want. But you can definitely execute console.log('called 5') after the async function; it's just you cannot use await (given that you're not in an async function):
async function delay(ms) {
await new Promise(r => setTimeout(r, ms));
console.log('called 3');
}
console.log('called 1');
(async () => {
console.log('called 2');
await delay(2000);
console.log('called 4');
})().then(
()=> console.log('called 5')
);
Don't forget that an async function returns an implicit Promise as result; so you can use then to obtain the result you want.
This, or you wrap everything in an async function so you can use await; but I understand from your question this is somehow not an option.

Categories

Resources