If you want to execute code after a callback is called in JavaScript, then you can just place it after the callback:
function demoCallback(callback) {
callback()
console.log("I execute second")
}
demoCallback(() => {
console.log("I execute first")
})
Is it possible to do the same thing with an ES6 Promise from within the scope of the function? Let's say I have a function that returns a Promise:
function demoPromise() {
return new Promise((resolve, reject) => {
resolve()
console.log("I execute first")
})
}
demoPromise().then(() => { console.log("I execute second") })
The Code inserted after resolve executes once the Promise is resolved, but before then is called outside the scope of the function. Is there a way that I can execute code after both, but do so from within the scope of the function?
Is it possible to do the same thing with an ES6 Promise from within the scope of the function?
No, this isn't possible. then callbacks always run asynchronously, and that includes being asynchronous in regard to the resolve() call.
(That said, promise callbacks are queued, so you can abuse that queue to get your code behind the other:
function demoPromise() {
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
p.then(() => {
console.log("I execute second");
}); // because I will be scheduled second
}, 1000);
});
return p;
}
demoPromise().then(() => {
console.log("I execute first");
}); // because I was scheduled first
But please don't do that)
If you want to execute code after a callback is called in JavaScript
then you probably should not just return a promise. Take a callback that does what you want before executing your code:
function demoPromise(callback) {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
}).then(() => {
return callback();
}).then(res => {
console.log("I execute second");
});
}
demoPromise(() => {
console.log("I execute first");
}).then(() => {
console.log("I execute last");
});
This is known as the disposer pattern and very useful for handling resources.
You can resolve the Promise with a function that can be called in a then block. So long as that function is called within the block, you can execute code before it like so
function demoPromise() {
return new Promise((resolve, reject) => {
resolve(function(){
console.log("I execute second")
});
})
}
demoPromise().then(f => { console.log("I execute first");f();})
Related
I have a function that returns a Promise. When I am done using that Promise in the .then() or .catch() blocks, I always want to execute the same cleanup code. My current setup is this:
const promiseWithFinally = () => {
return new Promise((resolve, reject) => {
// resolve or reject at some point
}).finally(() => console.log('finally done'))
}
promiseWithFinally()
.then(() => console.log('then done'))
.catch(() => console.log('catch done'))
What I want to happen is that either then done or catch done get logged first and then finally done. However it seems to get executed in the exact opposite order - when I resolve the Promise after a timeout of 5 seconds finally done gets logged first after 5 seconds and then then done immediately afterwards.
What am I doing wrong or is it possible to do this in general? I know I could just append the .finally() to each individual function call, but since it's always the same I'd like to put it in the function definition.
No it's not possible. Finally is for cleaning up after a given promise, not for its then or catch methods.
What you can do is pass then and catch methods to the function which will be appended before finally:
const promiseWithFinally = (chain) => {
return new Promise((resolve, reject) => {
// resolve or reject at some point
setTimeout(resolve, 1000);
}).then(chain.then, chain.catch).finally(() => console.log('finally done'))
}
promiseWithFinally({
then: () => console.log('then done'),
catch: () => console.log('catch done')
})
Short answer
No it is not possible as you cannot rely on when finally is being ran.
Longer answer and possible solution
Code
const cleanupFunc = () => {
console.log('Cleaning up.');
};
const someAsyncMethod = (thenFunc, catchFunc) => {
new Promise(
(resolve, reject) => {
setTimeout(() => resolve(), 5000);
},
)
.then((...args) => {
try {
thenFunc(...args);
} catch (err) {
}
cleanupFunc();
})
.catch((...args) => {
try {
catchFunc(...args);
} catch (err) {
}
cleanupFunc();
});
};
someAsyncMethod((result) => console.log('Then done'), (err) => console.log('Catch done'));
Explanation
Al though it is not possible to rely on finally, what you can do is write a function that needs to do some asynchronous operation returning a promise. In my example this operation is waiting on a 5 second timeout but his can also, for example, be an asynchronous api call that returns a promise.
The next step would be to add a then and a catch call to the promise the asynchronous operation returns that both begin with a try clause in which you call the callback parameter that belongs to the type of resolve (thenFunc for then, catchFunc for catch) followed by a catch which doesn't do anything and ends by calling the cleanup function. in this way you are certain that whatever happens during running the then or catch callback, the cleanup function is being called.
Assuming you know the rest of the handlers to that promise are going to be attached synchronously, and all actions in the handler are synchronous, it is possible, albeit a little bit hacky.
Simply have the finally handler re-attach itself at the end:
const promiseWithFinally = () => {
const thisPromise = new Promise((resolve, reject) => {
// Rejection example
setTimeout(reject, 200);
}).finally(() => {
setTimeout(() => {
thisPromise.finally(() => console.log('finally done')).catch(() => {});
}, 0);
});
return thisPromise;
};
promiseWithFinally()
.then(() => console.log('then done'))
.catch(() => console.log('catch done'));
I have a simple yet perplexing issue with async functions.
I wish to simply return the value when its ready from the function.
Here is a sample code:
async function test() {
setTimeout(function() {
return 'eeeee';
}, 5000);
}
test().then(x => {
console.log(x)
});
You will get undefined been logged at once.
It's clear that you are trying to write a sleep() async function, but do remember that setTimeout is a sync function calling with a callback function will be executed at a given time, so while you are executing test(), the calling will run to end and return undefined as you have no return statement in the function body, which will be passed to your .then() function.
The right way to do this is to return a Promise that will be resolved after a given time, that will continue the then call.
async function sleep(time){
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve("echo str")
},time)
})
}
sleep(5000).then((echo) => console.log(echo))
sleep function in short
const sleep = async time => new Promise(resolve=>setTimout(resolve,time))
With Promises
const setTimer = (duration) => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Done!');
}, duration);
});
return promise;
};
setTimer(2000).then((res) => console.log(res));
An async function has to return a promise. So to fix this, you can wrap your setTimeout function in a new promise like so:
async function test(){
return await new Promise((resolve, reject) => {
setTimeout(function(){
resolve('eeeee');
},5000);
})
}
test().then(x => {
console.log(x)
});
You can learn more about async/await on the MDN docs here. Hope this helps!
async function pending() {
return new Promise((resolve, reject) => { resolve(1) });
}
async function fulfilled() {
return 1;
}
function promiseState(p) {
return Promise.race([ Promise.resolve(p).then(() => "fulfilled", () => "rejected"), Promise.resolve().then(() => "pending") ]);
}
promiseState(pending()).then(s => { console.log(s); }); // pending
promiseState(fulfilled()).then(s => { console.log(s); }); // fulfilled
pending().then(r => { console.log(r); }); // 1
fulfilled().then(r => { console.log(r); }); // 1
What's different?
When should I use 'return new Promise(...' in async function? and Why?
It's the same as the difference between
function pending() {
return Promise.resolve(Promise.resolve(1));
}
and
function fulfilled() {
return Promise.resolve(1);
}
The former just takes one promise tick longer to settle.
When should I use 'return new Promise(...' in async function?
Probably never. See How to turn this callback into a promise using async/await? and What is the benefit of prepending async to a function that returns a promise?.
You should not construct Promises explicitly inside another Promise or async function, thats the promise constructor antipattern. It just adds additional overhead and you get no benefit whatsoever. If you somewhen really need the Promise constructor (for wrapping a callback API), then the surounding function should not be async, just return the promise.
I'm trying to understand JavaScript async/await. How can I rewrite the below such that the output is "Hi" then "Bye" instead of "Bye" then "Hi":
JSFiddle
sayHi()
.then(sayBye);
async function sayHi() {
await setTimeout(function() {
$("#myOutput").text('hi');
}, 1000);
}
async function sayBye() {
$("#myOutput").text('bye');
}
In order to await setTimeout it needs to be wrapped into Promise. Then with async/await you can flatten your code write it without Promise then API:
(async () => { // await has to be inside async function, anonymous in this case
await sayHi()
sayBye()
})()
async function sayHi() {
return new Promise(function (resolve) {
$("#myOutput").text('hi');
setTimeout(function() {
resolve()
}, 1000)
});
}
async function sayBye() {
$("#myOutput").text('bye');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myOutput"></div>
setTimeout doesn't return a Promise. Create a helper function to wrap it in a Promise and then you can await it.
function delay(fn, t) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(fn());
}, t);
});
}
sayHi()
.then(sayBye);
async function sayHi() {
await delay(() => {
//$("#myOutput").text('hi');
console.log("Hi");
}, 1000);
}
async function sayBye() {
//$("#myOutput").text('bye');
console.log("Bye");
}
Use the Promise way
sayHi()
.then(sayBye);
function sayHi() {
return new Promise(resolve => {
setTimeout(()=> {
$("#myOutput").text('hi'), resolve()
}, 1000);
})
}
async function sayBye() {
$("#myOutput").text('bye');
}
or the sayHi like this:
async function sayHi() {
await new Promise(resolve => {
setTimeout(()=> {
$("#myOutput").text('hi'), resolve()
}, 1000)
})
}
Using async/await is an excellent way to build acynchronous code in a quite controllable way. Promise based async function goes into microtasks depository, which event loop executes before events and methods contained in ordinary DOM refresh/web API depository (setTimeout() for example). However some versions of Opera and Firefox browsers set priority to setTimeout() over microtasks depository. Anyway, you can control order of execution if you combine Promise based function with async/await enabled function. For example:
// create function that returns Promise
let hi = () => {
return new Promise((resolve, reject) => {
setTimeout(_ => {
resolve('Hi '); // after 1500ms function is resolved and returns 'Hi '
}, 1500);
});
}
// create ordinary function that will return 'bye'
let sayBye = () => {
return 'Bye';
}
// create third function that will be async 'enabled',
// so it can use await keyword before Promise based functions
let sayHi = async () => {
let first = await hi(); // we store 'Hi ' into 'first' variable
console.log(first);
let second = sayBye(); // this execution will await until hi() is finished
console.log(second);
}
// execute async enabled function
sayHi();
We can add try / catch block inside sayHi() function for controlling error on promise reject(), but this is out of scope of your question.
Have a nice day!
You cannot use async/await for functions that are not returning Promise
When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.
An async function can contain an await expression, that pauses the execution of the async function and waits for the passed Promise's resolution, and then resumes the async function's execution and returns the resolved value.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Usually it is used to handle data that comes from server, because when you have multiple queries it can override previous one and you will handle the wrong one.
Async/await lets you handle exactly data you are awaiting for.
I test ES6 Promise in react APP, but i've not result on my .then :
function addItem (value) {
return new Promise((resolve, reject) => {
document.getElementById('todo').innerHTML += `<li>${value}</li>`;
});
}
addItem(value).then(() => {
alert('then !');
}).catch((err) => {
console.log(err)
})
My function is executed, but i've not event on my then.
Do you know where i was wrong ? Thank you ! :)
Promises are used for asynchronous tasks. There's no point in using them to execute synchronous code. In you example, then() is not working because you never resolve the promise. In order to resolve it, you need to call resolve().
You need to call either reject or resolve in your promise.
function addItem (value) {
return new Promise((resolve, reject) => {
document.getElementById('todo').innerHTML += `<li>${value}</li>`;
resolve();
});
}
You need to resolve your promise, for example:
function addItem (value) {
return new Promise((resolve, reject) => {
document.getElementById('todo').innerHTML += `<li>${value}</li>`;
resolve(10);
});
}
addItem(value).then((data) => {
alert('then !');
console.log(data);// 10
}).catch((err) => {
console.log(err)
})
You forgot to call the resolve and reject function in your Promise.
resolve is a function that you call when you're done with your action.
resolve();
The argument that you pass to resolve() is what you get back in the then function.
reject is a function that you call when there's a error with your action. If you don't have anything to reject then you can just leave it out and you won't have to call it.
reject();
the argument that you pass to reject() is what you get back in the catch function.