I would like to execute functions one at a time and call another function when a function is finished. I was able to do it using callbacks but not using a promise chain.
Here is what I tried (based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#chained_promises) but it executes the first 3 functions at the same time instead of waiting 1 second in each function:
function displayAll() {
var myPromise = (new Promise(display1))
.then((new Promise(display2))
.then((new Promise(display3))
.then(display4)));
}
function display1(resolve) {
setTimeout(function () {
console.log("display1");
resolve();
}, 1000);
}
function display2(resolve) {
setTimeout(function () {
console.log("display2");
resolve();
}, 1000);
}
function display3(resolve) {
setTimeout(function () {
console.log("display3");
resolve();
}, 1000);
}
function display4(resolve) {
setTimeout(function () {
console.log("display4");
}, 1000);
}
Do you know what is wrong with the code and if it is possible to do what I am trying to do without callbacks?
In order to chain Promises (MDN) you need to return a promise in then method callback, instead you are constructing the promise as an argument of the then method.
This will trigger the Promise as soon as is "encountered" the new keyword, that is not the expected behaviour. You instead want to wait the first Promise to end, and then chain the then method that will create a new Promise:
function displayAll() {
var myPromise = (new Promise(display1))
// .then(new Promise(display2)) <-- you are calling here the promise
.then(function() {
return new Promise(display2) // <-- here we return a promise to chain
})
.then(()=> new Promise(display3)) // same with arrow functions
.then(display4);
}
From your code:
function displayAll() {
var myPromise = (new Promise(display1))
.then(()=> new Promise(display2))
.then(() => new Promise(display3))
.then(display4);
}
function display1(resolve) {
setTimeout(function () {
console.log("display1");
resolve();
}, 1000);
}
function display2(resolve) {
setTimeout(function () {
console.log("display2");
resolve();
}, 1000);
}
function display3(resolve) {
setTimeout(function () {
console.log("display3");
resolve();
}, 1000);
}
function display4(resolve) {
setTimeout(function () {
console.log("display4");
}, 1000);
}
displayAll()
Another more clear approach:
You can also make your display functions return a Promise such that you can pass them directly to the then method:
function display1() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display1");
resolve();
}, 1000);
});
}
function display2() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display2");
resolve();
}, 1000);
});
}
function display3() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display3");
resolve();
}, 1000);
});
}
function display4() {
return new Promise(resolve => {
setTimeout(function () {
console.log("display4");
resolve();
}, 1000);
});
}
let myPromise =
display1()
.then(display2)
.then(display3)
.then(display4)
A walk through of displayAll's order of execution:
var myPromise = (new Promise(display1))
The Promise constructor calls display1 which sets up a timeout to log "display1" and resolve the promise. This works perfectly and the initial 1 second delay is honored.
.then(new Promise(display2))
calls the then method of myPromise during execution of displayAll.
The argument for then is evaluated before the making the call .
Creating then's promise argument causes the Promise constructor to call display2 which sets up a timeout relative to displayAll's time-of-execution.
When called then silently ignores the promise argument because it's not a function. Default handlers, used by then in the absence of callable arguments, pass through incoming data or promise rejection reasons.
.then(new Promise(display3))
operates the same as the previous then clause: set up a timer relative to displayAll's time-of-execution and use default handlers which pass through data or rejection reasons.
.then(display4)));
registers display4 as a handler to be called when the promise returned in step 3 becomes fullfilled. display4 sets up a workable timer to log "display4". Note display4's argument is now misnamed - the argument passed to successive fulfillment handlers is the value returned by the previous promise handler in the chain.
The expected output of calling displayAll is then to
Delay a second after displayAll was called and log "display1".
Delay a second, again after displayAll was called, and log "display2".
Delay a second, again after displayAll was called, and log "display3".
When promise chain handling executes display4 as a handler, set up a timer to log "display4" - so it logs one second after "display3".
One solution to stagger the execution of the display functions would be to move promise creation from where then arguments are calculated into the handler itself. The handler can return the promise to delay proceeding down the promise chain until the returned promise is resolved by the timer:
function displayN() {
return new Promise( resolve =>
setTimer( function() {
console.log("displayN");
resolve();
}, 1000)
);
}
Other solutions and approaches are possible. Promisifying setTimeout to create a promise that resolves after a specified interval can be useful for delaying steps in a promise chain or async function. As a fairly minimal example:
function delayPromise( msec) {
return new Promise(resolve=> setTimeout( resolve, msec));
}
As an aside only, the value of a promise chain is the promise returned by the last then, catch or finally call in the chain - promise chain values may be returned by a function but in practice at least are rarely recorded in a variable.
Related
I am looking at an implementation of a Promise class (at bottom). The constructor takes a function, f.
So if I have a regular function I want to pass in:
function myFunc() {
console.log("My Func");
}
If a Promise looks like this :
let promise = new Promise(resolve => {
myFunc();
}
then I am confused as to what f represents now though in the constructor of the class below. It appears as though resolve is the argument, not a function, f.
*Promise Class
class Promise {
constructor(f) {
this.onResolves = [];
this.status = "pending";
const resolve = value => {
debugger
this.status = "resolved";
this.value = value;
this.callOnResolves();
};
f(resolve);
}
callOnResolves() {
if (this.status === "resolved") {
this.onResolves.forEach(onResolve => onResolve(this.value));
this.onResolves.length = 0;
}
}
then(onResolve) {
return new Promise(resolve => {
this.onResolves.push(value => resolve(onResolve(value)));
this.callOnResolves();
});
}
}
f is the function that you pass to the promise's constructor. It takes a resolve parameter, which should eventually be called with the result of the asynchronous work.
Typically, your anonymous function would make use of an api that supports callbacks instead of promises, and in your callback to that api you would call resolve to let the Promise know that your asynchronous work is complete.
This is actually how you could implement an asynchronous sleep method using setTimeout.
function sleep(milliseconds) {
return new Promise(resolve => {
setTimeout(resolve, milliseconds);
});
}
sleep(1000).then(() => console.log(new Date()));
I'm trying to understand promises/asynchronous programming, and I don't understand why this code prints "1 2 0" rather than "2 1 0". From the third block of code: shouldn't f1 fire only after f2 logs "2" to the console?
const f1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(console.log(1));
}, 1000)
})
const f2 = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(console.log(2))
}, 1500)
})
f2.then(
f1.then(()=>{
setTimeout(()=>{
console.log(0);
}, 500)}
));
I'm trying to work on a program that needs several (like 6ish) asynchronous functions to fire in a very specific sequence, i.e. f2() must wait until f1() fully receives data from a request to execute, and I'm actually not sure how to do this...
You are running your f1 and f2 operations in parallel, starting them both one after the other. Both are started and then they are both in flight at the same time.
Then, you need to pass a function reference to .then() so it's a function that the promise infrastructure can call sometime in the future (when the host promise resolves). Instead, you are passing a promise to .then() which doesn't do anything useful.
If you change your code to this:
const f1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(console.log(1));
}, 1000)
})
const f2 = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(console.log(2))
}, 1500)
})
f2.then(() => {
f1.then(()=> {
setTimeout(()=>{
console.log(0);
}, 500)}
});
});
Then, you will start both the f1 and f2 timers immediately. Then, only after both f2 and f1 finish will you start the third timer and you should get the output 1 2 0. 1 comes first because it's a shorter timer than 2 which are both running in a parallel. 0 comes last because it's timer isn't started until both 1 and 2 are done.
If you want to chain them to get 2 1 0, then you have to not start 2 and 1 at the same time. Make f1 and f2 into functions, not promises so you can sequence when they are called.
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
function f1() {
return delay(1000).then(() => {
console.log(1);
})
}
function f2() {
return delay(1500).then(() => {
console.log(2);
})
}
function f3() {
return delay(1500).then(() => {
console.log(0);
})
}
// chain them together
f2().then(f1).then(f3).then(() => {
console.log("done");
});
This kind of .then() chaining basically says this:
Execute f2()
Register a .then() handler on the promise that .f2() returns so that when that promise resolves, it will then (and only then) call f1.
On the new promise that f2().then() returns, register a .then() handler on it so that when f1 is done, it will then call f3.
On the new promise that f2().then().then() returns, register a callback that will be called when f3 is done.
I have a series of promise functions that I'm chaining together. I don't need the specific result from the previous function so I'm not adding anything into resolve(); I just need them to run sequentially.
However, there is a variable in the containing scope that I want to pass into the first and fourth(last) promise. When I add it as a parameter to the third promise, the function runs immediately.
How can I pass a parameter into a chained promise and not call the function/promise at that time?
Here is the basics of what I'm trying to do:
const containingFunction = function() {
const varToPass = document.querySelector("#ID").value.trim();
firstPromise(varToPass)
.then(secondPromise)
.then(thirdPromise)
.then(fourthPromise(varToPass))
.catch(e =>{
console.error(e));
} );
};
FourthPromise:
const fourthPromise = function(varPassed) {
return new Promise(function(resolve, reject) {
do some stuff but only after the thirdPromise has been resolved
console.log(varPassed)
resolve();
});
};
You have two possibilities depending on how you want to resolve varToPass.
Lambda
Using a lambda function (anonymous function without own scope) as described by #Jaromanda X:
() => return fourthPromise(varToPass)
which will cause the function to keep a reference to the variable and not its value. The value of varToPass will be evaluated as soon as the fourthPromise is fired, not when this code is run.
Wrapper
The second option is using a wrapper, i.e. a function that returns a function:
function fourthPromise(varToPass) {
return function() {
return new Promise(function(resolve, reject) {
do some stuff but only after the thirdPromise has been resolved
console.log(varToPass)
resolve();
});
};
}
In this case the value of the passed variable is evaluated at the time this code runs and not when the callback is called.
Which option is better applicable to your case we can't tell without more context though.
Very simple change
const containingFunction = function() {
const varToPass = document.querySelector("#ID").value.trim();
firstPromise(varToPass)
.then(secondPromise)
.then(thirdPromise)
.then(() => fourthPromise(varToPass))
.catch(e =>{
console.error(e));
} );
};
In the following code, can someone explain why calling secondMethod in the Promise chain works but calling secondMethod() does not?
function firstMethod() {
return new Promise(function(resolve, reject){
setTimeout(function() {
console.log('first method completed');
resolve();
}, 2000);
});
};
function secondMethod() {
return new Promise(function(resolve, reject){
setTimeout(function() {
console.log('second method completed');
resolve();
}, 2000);
});
};
function thirdMethod() {
return new Promise(function(resolve, reject){
setTimeout(function() {
console.log('third method completed');
resolve();
}, 3000);
});
};
// Works
firstMethod().then(secondMethod).then(thirdMethod);
// Doesn't work - executes secondMethod immediately after firstMethod
// firstMethod().then(secondMethod()).then(thirdMethod);
The second way doesn't work because you're calling the functions immediately, in a synchronous way, before any of the timeouts resolve.
Here's another way to think about what's happening:
// you're calling all your methods and creating promises before creating the chain
let first = firstMethod();
let second = secondMethod();
let third = thirdMethod();
first.then(second).then(third);
Because Promise.then takes one or two callbacks. It does not take a Promise.
When you immediately call secondMethod you're passing a Promise to .then.
It's essentially the same as doing this:
firstMethod()
.then(new Promise(...)) // Should be a function, not a Promise
I need a initialization function called only one time for a module. This function is a promise and is called by an execute function. If execute is called twice, the second must wait the initialization then continue the execution.
I wrote this code, but the second call of execute is always waiting and doesn't never return. What have i missed?
var initialized = false;
var initializing = false;
var initializationPromise;
var init = function () {
initializing = true;
return q.promise(function (resolve) {
// simulate initialization
setTimeout(function () {
// initialized
initialized = true;
resolve();
}, 1000);
}).fin(function () {
initializing = false;
});
};
var execute = function () {
return q.promise(function (resolve, reject, notify) {
if (initialized) {
// already initialized
resolve();
} else {
if (!initializing) {
// initializing
initializationPromise = init().then(function () {
// simulate execution
setTimeout(function () {
resolve();
}, 1000);
}, function (reason) {
reject(reason);
});
} else {
// Wait : initializing in progress
return initializationPromise;
}
}
});
};
execute().then(function () {
// This is executed
});
execute().then(function () {
// This is never executed
});
// Wait : initializing in progress
return initializationPromise;
is not correct. That doesn't wait for anything, it just drops out of the q.promise constructor and does not do anything. Also you seem to employ the Promise constructor antipattern.
What you should do instead is
var initialisationPromise = null;
function isInitialised() {
return initialisationPromise != null && initialisationPromise.isFulfilled();
}
function isInitialising() {
return initialisationPromise != null && initialisationPromise.isPending();
}
function init() {
// init can be called as often as necessary, and returns when it's done
if (initialisationPromise == null) { // do the test here!
// this part runs only once
initialisationPromise = q.promise(function (resolve) {
// simulate initialization
setTimeout(function () {
// initialized
resolve();
}, 1000);
});
}
return initialisationPromise;
}
function execute() {
return init().then(function () {
return q.promise(function(resolve, reject, notify) {
// simulate execution
setTimeout(function () {
resolve();
}, 1000);
});
});
}
A resolved/rejected promise will maintain its state (resolved or rejected state), so you can use it to run the initialization code only once. To do that, the init() function should return always the same promise and not create it every time.
For this reason, we create a deferred object (initializationDeferred) outside the init() method and use initializationDeferred to return the same promise every time init() method is called. We need, also, to check if the init() has been already done before, we use the shared variable initializationStarted to skip the setTimeout if already done in a previous invocation.
Now, inside execute you can be sure that the onFulfilled callback of then() is called only when init() method is initialized.
var initializationDeferred = Q.defer(); // Create here the deferred object so it's common to all init() invocations
var initializationStarted = false;
var init = function() {
if (!initializationStarted) {
initializationStarted = true;
setTimeout(function() {
// initialized
console.log('Init timeout fired!');
initializationDeferred.resolve(true); // Resolve the promise associated to the deferred object
}, 1000);
}
return initializationDeferred.promise; // Return the promise associated to the deferred object
};
var execute = function() {
return init().then(function(initialized) {
// Here your module is initialized and you can do whatever you want
// The value of "initialized" here is always "true"
console.log('Execute: initialized?', initialized);
});
};
execute().then(function() {
// This is executed
console.log('Execute First invocation');
});
execute().then(function() {
// This is executed too
console.log('Execute Second invocation');
});
<script src="http://cdnjs.cloudflare.com/ajax/libs/q.js/0.9.2/q.js"></script>