I'd like to run the function that consists of:
if condition is solved
1)add class
2)sleep 4 sec then remove class
3)then stop running function using return.
function sleep(time) {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
function playGame() {
if (counter === 3) {
Promise.resolve().then(function () {
message.classList.add('winner');
sleep(2500).then(function () {
message.classList.remove('winner');
});
});
return;
}
...
}
If I run this code, function returning immediately of course. How do I add return to the promise chain.
This should work:
Promise.resolve()
.then(function () {
message.classList.add('winner');
})
.then(sleep(2500))
.then(function() {
message.classList.remove('winner');
});
This should be self-explanatory. Feel free to ask more questions as needed.
EDIT : Oooops it seems that I answered this one too quickly. You can never postpone a "return" statement in javascript. In your case, your code, or mine executes immediately, and the return of the enclosing function is called immediately, and the execution goes back to the event loop. Then, the first then() of the promise is called, etc...
Since you can't postpone the return of the function, you use callbacks, or better, Promises, to chain events:
function playGame() {
return Promise.resolve() // note the return !
.then(function () {
message.classList.add('winner');
})
.then(sleep(2500))
.then(function() {
message.classList.remove('winner');
});
}
...then
playGame()
.then(function() {
// this code is called after playGame's Promise has been executed
});
// this code is called when playGame ends, ie immediately, and before any promises code
To put it differently: once you begin to have asynchronous code (through callbacks or promises), every further code needs to be driven by promises, too.
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I have a parent function that calls a child function.
The child function will execute its internal code asynchronously so I'd like the parent function to call the child and then continue its own execution without waiting.
function childFunc() {
axios.post("http://api.example.com/user", { foo: "bar } ).
then((response) => {
// Do stuff here
console.log("SUCCESS");
return [1, 2, 3];
}).
catch((err) => {
// Do stuff here
console.log("ERROR");
return [];
});
console.log("About to return null");
}
function parentFunc(url, data) {
// Call childFunc but then continue execution
new Promise(function(resolve, reject) {
let result = childFunc(url, data);
result.length > 0 ? resolve(result) : reject("some error");
});
console.log("Continuing execution of `parentFunc()`");
// Continue doing other stuff
}
I'm just learning about Promise and async behavior in JavaScript, so I'm confused about the following:
What will childFunc return? If axios.post is executed asynchronously, wont the execution always continue past that and return null? Will the return statements inside then() or catch() ever fire? Does it return something twice?
Will the logic in parentFunc do what I expect it to do here: call childFunc asynchronously while simultaneously moving forward with its own execution? I've seen the async/await syntax, but wasn't sure how that really worked and whether that could be used here somehow.
Thanks!
What will childFunc return? If axios.post is executed asynchronously, wont the execution always continue past that and return null? Will the return statements inside then() or catch() ever fire? Does it return something twice?
childFunc returns nothing or undefined right now, because you don't return anything from it.
Will the logic in parentFunc do what I expect it to do here: call childFunc asynchronously while simultaneously moving forward with its own execution? I've seen the async/await syntax, but wasn't sure how that really worked and whether that could be used here somehow.
Sort of.. You don't need to explicitly use a Promise, axios already uses the JS Promise API and exposes Promise objects you can pass around.
So really, your code would ideally look like this:
function childFunc() {
return axios.post("http://api.example.com/user", { // childFunc now returns a Promise<Array<Number>>
foo: "bar" } ).
then((response) => {
// Do stuff here
console.log("SUCCESS");
return [1, 2, 3];
}).
catch((err) => {
// Do stuff here
console.log("ERROR");
return [];
});
// console.log("About to return null"); really would've been undefined, not null
}
function parentFunc(url, data) {
// Call childFunc but then continue execution
childFunc().then(results => {
console.log("These are the results of the child function", results)
// if you wanted to wait for childFunc to finish before continuing execution, put the statements to be executed after the childFunc execution here
}).catch(err => {
console.error("There was some error executing 'childFunc': " + err);
})
console.log("Continuing execution of `parentFunc()`");
// Continue doing other stuff
}
EDIT:
Added example of rolling your own/creating our own Promise:
function longRunningCall() {
return new Promise(function (resolve, reject) {
try {
setTimeout(function () {
console.log("Done");
resolve("done");
}, 3000);
} catch (error) { // this will never happen, but just to demonstrate handling errors asynchronously
reject("error");
}
});
}
longRunningCall().then(function(result) {
// the value of result will be "done";
}).catch(function(err) {
// if there was an error, the value of err would be "error"
})
I'm removing content (a div) but first waiting for an animation (if none is provided, then the animation is just a resolved Promise by default). Check this out:
clearContent = (animation = Promise.resolve()) => {
return new Promise((resolve, reject) => {
const child = $('#child');
animation.then(animationEvent => {
const eventPackage = {
'divRemoved': 'divIDPlaceholder',
'itemRemoved': 'contentIDPlaceholder',
'remainingItemsCount': 2,
'remainingItems': 1
};
child.remove();
const contentRemovedEvent = new CustomEvent('contentRemovedFromPlaceholder', {
'detail': eventPackage
});
window.dispatchEvent(contentRemovedEvent);
console.log('Removed!');
return resolve(eventPackage);
});
});
}
const testAnimationThatTakesASecond = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve()
}, 1000);
});
}
$('#child').on('click', () => {
clearContent(testAnimationThatTakesASecond());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="child">Remove me! The removal process can be animated as well!</div>
My constraints are that I'm waiting on an animation by anime.js and I'd like the whole removal process (that is the animation + the removal of the div itself) to be dependable on, because I might choose to chain some things based on it..
The problem is that although this is a wrapper function, I sometimes find that it doesn't work properly on low CPU. I'm thinking maybe because the .remove itself removes after the promise itself is resolved.
Am I wrong to think that a Promise will always wait for whatever code it has inside to finish?
Your function is fine, but from your last sentence and comments it seems like your expectation about promises is not quite correct.
If you are calling the code like this:
clearContent(testAnimationThatTakesASecond());
console.log('something after resolved');
The console.log will be executed before the animation finishes. The execution will not be halted in the calling context.
If you need to do something after the animation is resolved you need to use it like this:
clearContent(testAnimationThatTakesASecond())
.then(() => console.log('something after resolved'));
Or using async:
$('#child').on('click', async () => {
await clearContent(testAnimationThatTakesASecond());
console.log('something after resolved');
});
When you put async in front of a function you're telling that it will return a promise with the value from the return statement resolved!
When you await you're waiting for the promise to be fulfilled.
$('#child').on('click',async () => {
await testAnimationThatTakesASecond();
clearContent();
});
Edit:With promises also, we can use then to make sure that promise is resolved, and just then run the second phase.
Depend on our desired asynchronous order for our callbacks to get execute, if our they where doSomething() and doSomethingElse() to get them execute in order we can use
doSomething().then(doSomethingElse)
This is the only way that we are able to use the result of doSomething, inside doSomethingElse, It's like we ran:
doSomethingElse(resultOfDoSomething):
And also It's then-able in the way that if we had third operation, it get called after doSomethingElse finishes like: finalHandler(resultOfDoSomethingElse)
In testing I've found that JavaScript Promises are always asynchronous regardless of whether or not they contain any asynchronous functions in their chain.
Here is some code that shows the order of operations in console. If you run it you will see that even though every function is synchronous the output shows both of the aPromise() calls being run in parallel, and "surprisingly this happens after run 2 finishes" not happening before run 2 finishes.
function aPromise() {
return new Promise(function(resolve, reject) {
console.log("making promise A")
resolve(bPromise());
console.log("promise A resolved")
});
}
function bPromise() {
return new Promise(function(resolve, reject) {
console.log("making and resolving promise B")
resolve();
});
}
aPromise().then(function() {
console.log("finish run 1");
}).then(function() {
console.log("surprisingly this happens after run 2 finishes");
});
aPromise().then(function() {
console.log("finish run 2");
})
Output to console:
making promise A
making and resolving promise B
promise A resolved
making promise A
making and resolving promise B
promise A resolved
finish run 1
finish run 2
surprisingly this happens after run 2 finishes
So, Why are JavaScript promises asynchronous when calling only synchronous functions? What is happening behind the scenes that leads to this behavior?
P.S. In order to better understand this I implemented my own Promise system and I found that making synchronous functions happen in the expected order was easy but making them happen in parallel was something I could only accomplish by putting a setTimeout() of a few milliseconds at every resolve (My guess is that this is not what's happening with vanilla promises and that they are actually being multi threaded).
This has been a small problem for one of my programs where I'm traversing a tree applying an array of functions to each node and putting the functions in queue if that node has an asynchronous function already running. Most of the functions are synchronous so the queue is rarely used but upon switching over from callbacks (hell) to Promises I've been having an issue where the queues get used almost always as a result of Promises never running synchronously. It's not a huge problem but it is a bit of a debugging nightmare.
1 Year Later EDIT
I ended up writing some code to deal with this. It's not amazingly thorough, but I've used it with success to solve the issue I was having.
var SyncPromise = function(fn) {
var syncable = this;
syncable.state = "pending";
syncable.value;
var wrappedFn = function(resolve, reject) {
var fakeResolve = function(val) {
syncable.value = val;
syncable.state = "fulfilled";
resolve(val);
}
fn(fakeResolve, reject);
}
var out = new Promise(wrappedFn);
out.syncable = syncable;
return out;
}
SyncPromise.resolved = function(result) {
return new SyncPromise(function(resolve) { resolve(result); });
}
SyncPromise.all = function(promises) {
for(var i = 0; i < promises.length; i++) {
if(promises[i].syncable && promises[i].syncable.state == "fulfilled") {
promises.splice(i, 1);
i--;
}
// else console.log("syncable not fulfilled" + promises[i].syncable.state)
}
if(promises.length == 0)
return SyncPromise.resolved();
else
return new SyncPromise(function(resolve) { Promise.all(promises).then(resolve); });
}
Promise.prototype.syncThen = function (nextFn) {
if(this.syncable && this.syncable.state == "fulfilled") {
//
if(nextFn instanceof Promise) {
return nextFn;
}
else if(typeof nextFn == "function") {
var val = this.syncable.value;
var out = nextFn(val);
return new SyncPromise(function(resolve) { resolve(out); });
}
else {
PINE.err("nextFn is not a function or promise", nextFn);
}
}
else {
// console.log("default promise");
return this.then(nextFn);
}
}
The callback passed to a Promise constructor is always called synchronously, but the callbacks passed into then are always called asynchronously (you could use setTimeout with a delay of 0 in a userland implementation to achieve that).
Simplifying your example (and giving the anonymous function's names so I can refer to them) to:
Promise.resolve().then(function callbackA () {
console.log("finish run 1");
}).then(function callbackB () {
console.log("surprisingly this happens after run 2 finishes");
});
Promise.resolve().then(function callbackC () {
console.log("finish run 2");
})
Still gives the output in the same order:
finish run 1
finish run 2
surprisingly this happens after run 2 finishes
Events happen in this order:
The first promise is resolved (synchronously)
callbackA is added to the event loop's queue
The second promise is resolved
callbackC is added to the event loop's queue
There is nothing left to do so the event loop is accessed, callbackA is first in the queue so it is executed, it doesn't return a promise so the intermediate promise for callbackB is immediately resolved synchronously, which appends callbackB to the event loop's queue.
There is nothing left to do so the event loop is accessed, callbackC is first in the queue so it is executed.
There is nothing left to do so the event loop is accessed, callbackB is first in the queue so it is executed.
The easiest way I can think of to work around your problem is to use a library that has an Promise.prototype.isFulfilled function you can use to decide whether to call your second callback synchronously or not. For example:
var Promise = require( 'bluebird' );
Promise.prototype._SEPH_syncThen = function ( callback ) {
return (
this.isPending()
? this.then( callback )
: Promise.resolve( callback( this.value() ) )
);
}
Promise.resolve()._SEPH_syncThen(function callbackA () {
console.log("finish run 1");
})._SEPH_syncThen(function callbackB () {
console.log("surprisingly this happens after run 2 finishes");
});
Promise.resolve()._SEPH_syncThen(function callbackC () {
console.log("finish run 2");
})
This outputs:
finish run 1
surprisingly this happens after run 2 finishes
finish run 2
Your code is fine is you want your promises to run independently and let them execute in their own way, no matter each one complete first. As soon as your code is async, you cannot predict which one will be completed first (due to the async nature of the event loop).
However if you want to catch all your promises after they all completed, you should use Promise.all (which is the equivalent of $.when is jQuery).
See:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
This is a simple version of what I'm trying to do in my application. I have an if statement which evaluates the result of a function call and then populates an array if the statement comes back as true. AFTER the if statement is completely finished, I want to run some more code such as the console.log as seen below.
I understand that the if's evaluation is taking too long to finish and javascript just continues to the console.log because of its asynchronicity. How do I make the code wait for the if statement to complete?
var tabs = [];
if (isTrue()) {
tabs.push('some string');
}
console.log(tabs[1]);
function isTrue() {
setTimeout(function() {
return true;
}, 500)
}
You can just wrap your code in a Promise and consume the returned values by calling then on it:
var tabs = [];
isTrue().then(res => {
if (res) {
tabs.push('some string');
}
return tabs;
}).then(arr => {
console.log(arr);
});
function isTrue() {
//Just wrap your existing code in a Promise constructor
return new Promise((resolve, reject) => {
setTimeout(() => {
//Pass whatever value you want to consume later to resolve
resolve(true);
}, 500)
});
}
You could pass a callback to the isTrue() function, something like:
function isTrue(_callback) {
setTimeout(function() {
// code here
// Call the callback when done
if (typeof(_callback) === 'function')
_callback(tabs);
});
}
function showTabs(tabs) {
console.log(tabs[1]);
}
isTrue(showTabs);
Ought to work.
Using modern javascript, you can achieve that using promises and async/await:
const isTrue = () => new Promise(resolve => setTimeout(resolve, 500, true));
// you can only use `await` inside an `async` function
async function main() {
// better use `let` instead of `var` since `let` is block scoped,
// see:
// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let>
let tabs = [];
if (await isTrue()) {
tabs.push('some string');
}
// array's index start by 0, not 1
console.log(tabs[0]);
}
main();
(this code also use arrow functions for isTrue.)
isTrue() returns undefined. The return true inside of the setTimeout callback will return back to the timeout call, not to the isTrue() call. The code executes immeadiately and there is no asynchronity involved (except for that timer that does nothing).
I've been learning promises using bluebird for two weeks now. I have them mostly understood, but I went to go solve a few related problems and it seems my knowledge has fell apart. I'm trying to do this simple code:
var someGlobal = true;
whilePromsie(function() {
return someGlobal;
}, function(result) { // possibly even use return value of 1st parm?
// keep running this promise code
return new Promise(....).then(....);
});
as a concrete example:
// This is some very contrived functionality, but let's pretend this is
// doing something external: ajax call, db call, filesystem call, etc.
// Simply return a number between 0-999 after a 0-999 millisecond
// fake delay.
function getNextItem() {
return new Promise.delay(Math.random()*1000).then(function() {
Promise.cast(Math.floor(Math.random() * 1000));
});
}
promiseWhile(function() {
// this will never return false in my example so run forever
return getNextItem() !== false;
}, // how to have result == return value of getNextItem()?
function(result) {
result.then(function(x) {
// do some work ...
}).catch(function(err) {
console.warn("A nasty error occured!: ", err);
});
}).then(function(result) {
console.log("The while finally ended!");
});
Now I've done my homework! There is the same question, but geared toward Q.js here:
Correct way to write loops for promise.
But the accepted answers, as well as additional answers:
Are geared toward Q.js or RSVP
The only answer geared toward bluebird uses recursion. These seems like it's likely to cause a huge stack overflow in an infinite loop such as mine? Or at best, be very inefficient and create a very large stack for nothing? If I'm wrong, then fine! Let me know.
Don't allow you to use result of the condition. Although this isn't requirement -- I'm just curious if it's possible. The code I'm writing, one use case needs it, the other doesn't.
Now, there is an answer regarding RSVP that uses this async() method. And what really confuses me is bluebird documents and I even see code for a Promise.async() call in the repository, but I don't see it in my latest copy of bluebird. Is it in the git repository only or something?
It's not 100% clear what you're trying to do, but I'll write an answer that does the following things you mention:
Loops until some condition in your code is met
Allows you to use a delay between loop iterations
Allows you to get and process the final result
Works with Bluebird (I'll code to the ES6 promise standard which will work with Bluebird or native promises)
Does not have stack build-up
First, let's assume you have some async function that returns a promise whose result is used to determine whether to continue looping or not.
function getNextItem() {
return new Promise.delay(Math.random()*1000).then(function() {
return(Math.floor(Math.random() * 1000));
});
}
Now, you want to loop until the value returned meets some condition
function processLoop(delay) {
return new Promise(function(resolve, reject) {
var results = [];
function next() {
getNextItem().then(function(val) {
// add to result array
results.push(val);
if (val < 100) {
// found a val < 100, so be done with the loop
resolve(results);
} else {
// run another iteration of the loop after delay
setTimeout(next, delay);
}
}, reject);
}
// start first iteration of the loop
next();
});
}
processLoop(100).then(function(results) {
// process results here
}, function(err) {
// error here
});
If you wanted to make this more generic so you could pass in the function and comparison, you could do this:
function processLoop(mainFn, compareFn, delay) {
return new Promise(function(resolve, reject) {
var results = [];
function next() {
mainFn().then(function(val) {
// add to result array
results.push(val);
if (compareFn(val))
// found a val < 100, so be done with the loop
resolve(results);
} else {
// run another iteration of the loop after delay
if (delay) {
setTimeout(next, delay);
} else {
next();
}
}
}, reject);
}
// start first iteration of the loop
next();
});
}
processLoop(getNextItem, function(val) {
return val < 100;
}, 100).then(function(results) {
// process results here
}, function(err) {
// error here
});
Your attempts at a structure like this:
return getNextItem() !== false;
Can't work because getNextItem() returns a promise which is always !== false since a promise is an object so that can't work. If you want to test a promise, you have to use .then() to get its value and you have to do the comparson asynchronously so you can't directly return a value like that.
Note: While these implementations use a function that calls itself, this does not cause stack build-up because they call themselves asynchronously. That means the stack has already completely unwound before the function calls itself again, thus there is no stack build-up. This will always be the case from a .then() handler since the Promise specification requires that a .then() handler is not called until the stack has returned to "platform code" which means it has unwound all regular "user code" before calling the .then() handler.
Using async and await in ES7
In ES7, you can use async and await to "pause" a loop. That can make this type of iteration a lot simpler to code. This looks structurally more like a typical synchronous loop. It uses await to wait on promises and because the function is declared async, it always returns a promise:
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
async function processLoop(mainFn, compareFn, timeDelay) {
var results = [];
// loop until condition is met
while (true) {
let val = await mainFn();
results.push(val);
if (compareFn(val)) {
return results;
} else {
if (timeDelay) {
await delay(timeDelay);
}
}
}
}
processLoop(getNextItem, function(val) {
return val < 100;
}, 100).then(function(results) {
// process results here
}, function(err) {
// error here
});