This question already has answers here:
Aren't promises just callbacks?
(11 answers)
Closed 29 days ago.
I'm just discovering Promises and I'm trying to wrap my head around them. The W3C website has a page dedicated to it, featuring the following code :
let myPromise = new Promise(function(myResolve, myReject) {
let x = 0;
// The producing code (this may take some time)
if (x == 0) {
myResolve("OK");
} else {
myReject("Error");
}
});
myPromise.then(
function(value) {myDisplayer(value);},
function(error) {myDisplayer(error);}
);
I tried to make my own try at it, with the following code :
let p = new Promise(function test(resolve, reject){
// here is supposed to be where the async code takes place, according to the
// article.
let a = 0;
setTimeout(() => {a = Math.floor(Math.random()*6)}, "1000")
// ...however the condition triggers before the setTimeout takes place.
if(a >= 0) {
resolve(`Success ! a = ${a}`);
} else {
reject(`Failure ! a = ${a}`);
}
});
p.then(function logResult(result){
console.log(result);
})
So I figured that this should work :
let q = new Promise(function test(resolve, reject){
let a = 0;
setTimeout(() => {
a = Math.floor(Math.random()*6);
if(a >= 4) {
resolve(`Success ! a = ${a}`);
} else {
reject(`Failure ! a = ${a}`);
}
}, "1000")
});
q.then(function logResult(result){
console.log(result);
})
And it does work, but it's the setTimeout callbacks that handles everything, not the promise itself, and doing it without the promise works just as fine :
let a = 0;
setTimeout(() => {
a = Math.floor(Math.random() * 6);
if (a >= 4) {
console.log(`Success ! a = ${a}`);
} else {
console.log(`Failure ! a = ${a}`);
}
}, "1000")
So there's definitely something I don't understand about how Promises are supposed to handle async code or why they're useful altogether. I wish someone would explain it to me.
The whole point of promises is to allow some other code to be executed while waiting for a promise to resolve or reject.
In your code, however, no action takes time; therefore, you can do the same without promises.
The most common scenario for promises is something like this:
Show the user a progress dialog
Request some data from the backend
Once data is retrieved, process it
After data is processed, show it to a user
There will be a delay between steps 2 and 3 because an HTTP request will take some time naturally. Also, step 3 may take some time to process, and we don't want to block our code execution while waiting for it to complete. In this case, we wrap step 2 in a promise and use then to execute steps 3 and 4.
Please let me know if this helps.
Promises are used to handle operations which take longer than others. For example fetching something from a server. Since JavaScript is single-threaded, promises and callbacks are the answer for problems with long executing operations. If you would fetch something from a server synchronously (without a promise or callback), you wouldn't be capable to interact with your app in any other way (Click handlers wouldn't work.). It would block the main thread for the time when it fetches the data. Promises and callbacks are executing when there is nothing left to execute. Using promise to wrap callback function might be used to utilize async/await and .then() with it. It's in addition to prevent what's called - callback hell (a lot of callbacks nested in each other).
Related
I'm doing some unit testing. The test framework loads a page into an iFrame and then runs assertions against that page. Before each test begins, I create a Promise which sets the iFrame's onload event to call resolve(), sets the iFrame's src, and returns the promise.
So, I can just call loadUrl(url).then(myFunc), and it will wait for the page to load before executing whatever myFunc is.
I use this sort of pattern all over the place in my tests (not just for loading URLs), primarily in order to allow changes to the DOM to happen (e.g. mimick clicking a button, and wait for divs to hide and show).
The downside to this design is that I'm constantly writing anonymous functions with a few lines of code in them. Further, while I have a work-around (QUnit's assert.async()), the test function that defines the promises completes before the promise is run.
I'm wondering if there is any way to get a value from a Promise or wait (block/sleep) until it has resolved, similar to .NET's IAsyncResult.WaitHandle.WaitOne(). I know JavaScript is single-threaded, but I'm hoping that doesn't mean that a function can't yield.
In essence, is there a way to get the following to spit out results in the correct order?
function kickOff() {
return new Promise(function(resolve, reject) {
$("#output").append("start");
setTimeout(function() {
resolve();
}, 1000);
}).then(function() {
$("#output").append(" middle");
return " end";
});
};
function getResultFrom(promise) {
// todo
return " end";
}
var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>
I'm wondering if there is any way to get a value from a Promise or
wait (block/sleep) until it has resolved, similar to .NET's
IAsyncResult.WaitHandle.WaitOne(). I know JavaScript is
single-threaded, but I'm hoping that doesn't mean that a function
can't yield.
The current generation of Javascript in browsers does not have a wait() or sleep() that allows other things to run. So, you simply can't do what you're asking. Instead, it has async operations that will do their thing and then call you when they're done (as you've been using promises for).
Part of this is because of Javascript's single threadedness. If the single thread is spinning, then no other Javascript can execute until that spinning thread is done. ES6 introduces yield and generators which will allow some cooperative tricks like that, but we're quite a ways from being able to use those in a wide swatch of installed browsers (they can be used in some server-side development where you control the JS engine that is being used).
Careful management of promise-based code can control the order of execution for many async operations.
I'm not sure I understand exactly what order you're trying to achieve in your code, but you could do something like this using your existing kickOff() function, and then attaching a .then() handler to it after calling it:
function kickOff() {
return new Promise(function(resolve, reject) {
$("#output").append("start");
setTimeout(function() {
resolve();
}, 1000);
}).then(function() {
$("#output").append(" middle");
return " end";
});
}
kickOff().then(function(result) {
// use the result here
$("#output").append(result);
});
This will return output in a guaranteed order - like this:
start
middle
end
Update in 2018 (three years after this answer was written):
If you either transpile your code or run your code in an environment that supports ES7 features such as async and await, you can now use await to make your code "appear" to wait for the result of a promise. It is still programming with promises. It does still not block all of Javascript, but it does allow you to write sequential operations in a friendlier syntax.
Instead of the ES6 way of doing things:
someFunc().then(someFunc2).then(result => {
// process result here
}).catch(err => {
// process error here
});
You can do this:
// returns a promise
async function wrapperFunc() {
try {
let r1 = await someFunc();
let r2 = await someFunc2(r1);
// now process r2
return someValue; // this will be the resolved value of the returned promise
} catch(e) {
console.log(e);
throw e; // let caller know the promise was rejected with this reason
}
}
wrapperFunc().then(result => {
// got final result
}).catch(err => {
// got error
});
async functions return a promise as soon as the first await is hit inside their function body so to the caller an async function is still non-blocking and the caller must still deal with a returned promise and get the result from that promise. But, inside the async function, you can write more sequential-like code using await on promises. Keep in mind that await only does something useful if you await a promise so in order to use async/await, your asynchronous operations must all be promise-based.
If using ES2016 you can use async and await and do something like:
(async () => {
const data = await fetch(url)
myFunc(data)
}())
If using ES2015 you can use Generators. If you don't like the syntax you can abstract it away using an async utility function as explained here.
If using ES5 you'll probably want a library like Bluebird to give you more control.
Finally, if your runtime supports ES2015 already execution order may be preserved with parallelism using Fetch Injection.
Another option is to use Promise.all to wait for an array of promises to resolve and then act on those.
Code below shows how to wait for all the promises to resolve and then deal with the results once they are all ready (as that seemed to be the objective of the question); Also for illustrative purposes, it shows output during execution (end finishes before middle).
function append_output(suffix, value) {
$("#output_"+suffix).append(value)
}
function kickOff() {
let start = new Promise((resolve, reject) => {
append_output("now", "start")
resolve("start")
})
let middle = new Promise((resolve, reject) => {
setTimeout(() => {
append_output("now", " middle")
resolve(" middle")
}, 1000)
})
let end = new Promise((resolve, reject) => {
append_output("now", " end")
resolve(" end")
})
Promise.all([start, middle, end]).then(results => {
results.forEach(
result => append_output("later", result))
})
}
kickOff()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Updated during execution: <div id="output_now"></div>
Updated after all have completed: <div id="output_later"></div>
This question already has answers here:
Correct way to write a non-blocking function in Node.js
(2 answers)
Closed 1 year ago.
Could someone try and help me to understand why the first function is non-blocking while the second one blocks the rest of the code? Isn't Promise.resolve the same as resolving from a new Promise? I can't quite wrap my head around it.
function blockingCode() {
return new Promise((resolve, reject) => {
for (let i = 0; i < 2500000000; i++) {
// Doing nothing...
}
resolve('ping');
});
}
function nonBlockingCode() {
return Promise.resolve().then(() => {
for (let i = 0; i < 2500000000; i++) {
// Doing nothing...
}
return 'pong';
});
}
console.time('non-blocking');
nonBlockingCode().then((message) => console.log(message));
console.timeEnd('non-blocking');
// console.time('blocking');
// blockingCode().then((message) => console.log(message));
// console.timeEnd('blocking');
The two functions are actually blocking.
You have the illusion that the second one isn't blocking because calling Promise.resolve().then() adds one more round to the event loop, so console.timeEnd('non-blocking'); is reached before the code inside your promise even starts.
You will notice that both function block if you fix your code like this:
console.time('non-blocking');
nonBlockingCode()
.then((message) => console.log(message))
.then(() => console.timeEnd('non-blocking'));
Note that a promise is intended not to block when the time-consuming logic inside the promise is delegated to a third-party, and you just wait for the result (e.g. when a client does a call to a remote database, a remote web server, etc.).
If you are having a hard time with promises and then logic, have a look at the async / await syntax, I find it much easier to understand.
So basically I want to asynchronously execute 2 synchronous promises. Like so:
(function foo() {
var newValues = ...;
this.getMessages().then((msgs: Array<any>) => {
this.saveMessages(msgs.concat(newValues));
});
})()
Now since I don't want to wait for saveMessages to finish execution I didn't add a .then() at the end. Since I don't care about the result. I just want to async function to execute so at some point we have the messages offline.
BUT I fear that the promise might get garbage collected since by the standard (as far as I know) you always need a .then for promises.
So my question is whether I need to add an empty .then to make sure it's not garbage collected too early and the function wouldn't execute correctly. Like so: this.saveMessages(msgs.concat(newValues)).then();?
And is it the same in the browser and NodeJS?
BUT I fear that the promise might get garbage collected since by the
standard (as far as I know) you always need a .then() for promises.
No. There does not exist any such standard -- to my knowledge -- which states that the GC will free up the memory allocated to the a Promise object if a .then() is not attached to it.
Promises will get executed regardless of whether a .then() -- or a .catch() for that matter -- is attached to it.
So my question is whether I need to add an empty .then() to make sure
it's not garbage collected too early and the function wouldn't execute
correctly.
No you do not need an empty .then()
And is it the same in the browser and NodeJS?
Yes, as long as you use same Promise implementation on both the runtimes.
So, you would be completely fine with this:
function foo() {
var newValues = ...;
this.getMessages().then((msgs: Array<any>) => {
this.saveMessages(msgs.concat(newValues));
});
}
But, You should consider attaching a .catch() to it to handle any errors. If you don't, you will have UnhandledPromiseRejectionWarning. In future versions of Node, it can crash your whole app. Read about it here
EDIT
Even if the parent has nothing to process or execute, the program will wait till each of the 100 promises has resolved. Try this:
const n = 100;
const sleep = (id, seconds) =>
new Promise(resolve => {
setTimeout(() => {
console.log(`promise with id ${id} is resolving`);
resolve();
}, seconds * 1000);
});
for (let i = 0; i < n; i++) {
sleep(i, 1 * i);
}
console.log("Last line of parent");
No, the Promise knows nothing about how you're handling its completion until it gets there.
You don't need to worry about garbage collection preemptively, Javascript is smarter than that, but you do have to worry about the global execution context completing, as in, your program closing the connection, before the Promise has resolved, which would then cancel the promise and perform garbage collection.
Since you're running it asynchronously, if you want to guarantee the program awaits the completion of that Promise without making everything synchronous you can always hold the completion state of the Promises on a scoped variable or use an event emitter...
var imDone = false;
function foo() {
var newValues = ...;
this.getMessages().then((msgs: Array<any>) => {
this.saveMessages(msgs.concat(newValues))
.catch(err=> { console.log(err); imDone = true; })
.then(()=> imDone = true);
}).catch(err=> console.log(err));
}
....
if (imDone) {
// finish execution, res.send() or res.end() in Node
}
you can use this code and return your first promise and second promise is run sync.
function foo() {
var newValues = ...;
return this.getMessages().then((msgs: Array<any>) => {
this.saveMessages(msgs.concat(newValues));
});
}
You are not executing the code in the right way. Promises should be handled in the right way.There is no issue with the garbage collector freeing up the space. Your code can be written in this manner which would increase the readability and make the code easier to understand.
// This is pseudo synchronous execution of the code
function foo() {
var newValues = 'testing';
return this.getMessages()
.then((msgs: Array<any>) => {
// here return is important
return this.saveMessages(msgs.concat(newValues));
});
}
You can reduce the complexity by using async/await feature:
aync function foo() {
try {
var newValues = 'testing';
const msgs = await this.getMessages()
await this.saveMessages(msgs.concat(newValues))
} catch (ex) {
// handle your error here
}
}
This question already has answers here:
How to synchronize a sequence of promises?
(6 answers)
Closed 5 years ago.
I have a promise chain problem I have been grappling with. I make a call out to an external api that returns me data that I need to process and ingest into a mongo database. I am using nodejs and mongodb with express. Anyway, the calls to the api are working ok, the problem is that I am making tons of them at once. I want to slow them down, like make all the calls for one set. Wait a minute. Make all the calls for the next set. If this was a known amount of sets I would just promise chain them. I dont know how many sets there are so I am looping through them. I think the closure is the problem but cant work around it. on to the example code!
function readApi(carFactory){
var promise = new Promise(function(resolve, reject) {
// call out to api, get set of car data from factory1
console.log(carFactory);
if (true) {
console.log('resolved');
resolve("Stuff worked!"+carFactory);
}
else {
reject(Error("It broke"));
}
});
return promise;
}
function manager(){
//singular call
readApi("this is a singular test").then(returnedThing=>{
console.log(returnedThing); //Stuff worked! this is a singular test
});
let dynamicList = ["carFactory1", "carFactory2","carFactory3","carFactory..N"];
let readApiAction = [];
dynamicList.forEach(carIter=>{
readApiAction.push(readApi(carIter));
});
//ok so now I got an array of promise objects.
//I want to call the first one, wait 1 minute and then call the next one.
//originally I was calling promise.all, but there is no way to get at
//each promise to pause them out so this code is what I am looking to fix
let results= Promise.all(readApiAction);
results.then(data=>{
data.forEach(resolvedData=>{
console.log(resolvedData); //Stuff worked carFactory1, etc...
});
});
//singular call with timeout, this does not work, each one called at the same time
let readApiActionTimeouts = [];
dynamicList.forEach(carIter=>{
setTimeout(callingTimeout(carIter), 6000);
});
}
//just a function to call the promise in a timeout
//this fails with this - TypeError: "callback" argument must be a function
function callingTimeout(carIter){
readApi(carIter).then(returnedThing=>{
console.log("timeout version"+returnedThing);
});
}
A bit on theory. Native Promise.all just groups promises. They're still executed simultaneously (in async way, though, as all JS code, but along each other). This means that it will still congest the API and perform a lot of calls.
Another thing to note is that if you want to delay a promise, you have to delay its return value (e.g. resolve). In order to do so, you may use setTimeout INSIDE new Promise (just look below for more explanation).
Set timeout is asynchronous. It does not work as in other languages (it does not just pause execution). Setting fixed timeout in your code just caused to move ALL the executions by 6s. They still happened in parallel (in different ticks, though, but it's a small difference). Try e.g. generating different timeouts for each one in the loop - you'll notice that they're happening in a different time BUT! This is not a good solution for promisified code!
And now - time for practical answer!
If you use Bluebird, it has a special method to add delay or timeout to each promise. Without it, you would have to write a wrapper around Promise, e.g. resolving it after a particular amount of time and then use it with Promise.all.
First solution (bluebird):
function delayMyPromise(myPromise, myDelay);
return Promise.delay(myDelay).then(function() {
return myPromise;
});
});
and then in your code:
return Promise.all(delayMyPromise(promise1, 1000), delayMyPromise(promise2, 2000)); // Notice different delays, you may generate them programatically
Or even cooler, you can use Promise.map from Bluebird instead of Promise.all that has a special concurrency setting so you may force your promises to be executed in particular sequence, e.g. 2 at a time.
This is how I did it on my previous project :)
More here:
http://bluebirdjs.com/docs/api/promise.delay.html
http://bluebirdjs.com/docs/api/map.html
Pure native Promise implementation:
function delayMyPromise(myPromise, myDelay) {
return new Promise(function (resolve, reject) {
setTimeout(function() {
return resolve(myPromise);
}, myDelay);
});
}
I'd, however, heavily recommend first approach if you don't mind using Bluebird. It's like lodash for Promises and it's really fast :)
you get the error : TypeError: "callback" argument must be a function because your callingTimeout returns nothing and setTimeout needs a function as argument, this is how to fixe it :
let readApiActionTimeouts = [];
dynamicList.forEach(carIter=>{
callingTimeout(carIter)
});
your promise :
function readApi(carFactory){
var promise = new Promise(function(resolve, reject) {
//...
setTimeout(()=>{
resolve("Stuff worked!"+carFactory);
}, 6000);
//...
});
return promise;
}
You could use recursion for something like this.
When you call .forEach, each iteration happens immediately.
In the example below, doSomething is not called until the setTimeout occurs, which means each letter is printed 1 second apart.
let letters = ["a", "b", "c"];
function doSomething(arr) {
console.log(arr[0]);
if (arr.length > 1) {
setTimeout(() => doSomething(arr.slice(1)), 1000);
}
}
doSomething(letters);
Alternately, for your array of promises:
let promises = [
Promise.resolve("A"),
Promise.resolve("B"),
Promise.resolve("C"),
];
function doSomething(arr) {
arr[0].then((result) => {
console.log(result);
if (arr.length > 1) {
setTimeout(() => doSomething(arr.slice(1)), 1000);
}
})
}
doSomething(promises);
I am looking for a way to run some code in nodejs after N amount of seconds.
Tried setTimeout() but it seems to completely block on it until the time is out but this is not what I want since my server is still sending and receiving events.
Any advice?
Actually, setTimeout is asynchronous, so it will not block.
setTimeout(function(){
// this code will only run when time has ellapsed
}, n * 1000);
// this code will not block, and will only run at the time
Actually setTimeout() does exactly what you are asking for, it does not block and will execute the given function at some time in the future.
However, it can be tricky to understand what's going on in Node.js. I highly recommend making the investment in learning how to use the Promise API. It can be confusing at first, but gives you a very flexible structure for controlling ansynchronous events. Here is an example I wrote as part of learning how to use the Promise API. You will see that it actually uses setTimeout(), but embeds it in a Promise. Hopefully this code is self explanatory and helps you achieve what you need.
/*
* Try out javascript Promise code in Node.js
*
*/
"use strict";
function getRandomBoolean() {
return Math.random() > 0.5;
}
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
for (let i = 0; i < 5; i++) {
// Create a promise, which will randomly succeed (and call resolve) or
// fail (and call reject) after a random time interval
let intervalMS = getRandomInt(5000);
let promise = new Promise(function (resolve, reject) {
setTimeout(() => {
if (getRandomBoolean()) {
// Success - Call resolver function
resolve(i+" Hooray!");
} else {
// Treat this as an error - Call reject function
reject(i+" Sorry!");
}
}, intervalMS);
});
// When the promise is complete, show the results
promise.then(
// The first function is the resolve function, to be called
// with a result param upon successful completion of async task
result => console.log("Success: "+result),
// Next is reject function, to be called with an error parameter when something failed
error => console.log("Failure: "+error)
);
// Flow of execution falls through to here without blocking
console.log ("Created promise "+i+", will complete in "+intervalMS+" ms");
}
If you run the above example in Node.js (or actually it should run in the browser too, I just haven't tested it there) you will see output something like the following:
Created promise 0, will complete in 853 ms
Created promise 1, will complete in 2388 ms
Created promise 2, will complete in 2738 ms
Created promise 3, will complete in 3053 ms
Created promise 4, will complete in 652 ms
Success: 4 Hooray!
Failure: 0 Sorry!
Failure: 1 Sorry!
Success: 2 Hooray!
Success: 3 Hooray!
Note that the "Created promise..." output comes out first, showing you that the execution falls through without blocking.