In this simplified example, I am calling three functions when the first promise gets resolved.
var test = new Promise(function (res, err) { setTimeout(res, 3000) })
test.then( () => { console.log("A") });
test.then( () => {
return new Promise(function(res, err) {
setTimeout(()=> { console.log("C");
res() }, 3000)
});
});
test.then( () => { console.log("B") });
The output as expected is A B C.
Let's suppose that I want to invoke the third .then to console B only when the second promise gets resolved.
If I try to store the second promise (myProm) in global and attach a .then function on it, I'll get (reasonably) a TypeError because at compile time myProm is still undefined.
var test = new Promise(function (res, err) { setTimeout(res, 3000) })
var myProm;
test.then( () => { console.log("A") });
test.then( () => {
myProm = new Promise(function(res, err) {
setTimeout(()=> { console.log("C");
res() }, 3000)
})
return myProm;
});
myProm.then( () => { console.log("B") });
How do I proceed? What's the best way to chain two promises together so that the returned Promise obj from one .then needs to get resolved before we can execute the next then.
In this scenario, the output I'd like to have would be A C B
then returns a promise that resolves when the indicated function has run and the value it returned has resolved (this is a slight oversimplification, but sufficient for the task here).
Therefore, to chain a promise on the result of a then. Just tack on another .then:
var test = new Promise(function (res, err) { setTimeout(res, 3000) })
test.then( () => { console.log("A") });
test
.then( () => {
return new Promise(function(res, err) {
setTimeout(()=> { console.log("C"); res() }, 3000);
});
})
.then( () => { console.log("B") });
Each time you call .then, you create a new Promise that resolves when the Promise returned by that .then resolves. You should assign the result of the myProm-containing .then to a variable, and then call .then on that variable:
var test = new Promise(function (res, err) { setTimeout(res, 500) })
var myProm;
test.then( () => { console.log("A") })
.then( () => {
myProm = new Promise(function(res, err) {
setTimeout(()=> { console.log("C");
res() }, 500)
})
return myProm;
})
.then( () => { console.log("B") });
Most of the time when using Promises, you should be chaining .thens like this. Only do prom.then(...) ... prom.then(...) when you want to initialize two completely separate asynchronous operations when the prom resolves.
Related
I would like to resolve my promise after my setTimeout will be finished. I have a few functions like step The main idea is to get step results sequentially. P.S. we can't mutate setTimeout.
function step (done) {
return new Promise((resolve, reject) => {
try {
setTimeout(done, 5100, 'hello world')
resolve()
} catch (error) {
reject()
}
})
}
If you can't mutate the setTimeout remove it. Instead of putting the setTimeout in the step function create a new delay function that returns a promise. You can then use async/await to walk through the steps after certain delays.
function delay(time = 1000) {
return new Promise(res => {
setTimeout(() => res(), time);
});
}
function step(fn, n) {
fn(`hello world ${n}`);
}
const fn = (str) => console.log(str);
async function main() {
step(fn, 1);
await delay(5000);
step(fn, 2);
await delay(3000);
step(fn, 3);
}
main();
If you can change the done function, you can do like below.
function step(done) {
return new Promise((resolve, reject) => {
immutableFunc(done(resolve, reject));
});
}
function immutableFunc(done) {
setTimeout(done, 1000, "hello world");
}
function done(resolve, reject) {
return arg => {
try {
console.log(arg);
resolve();
} catch (error) {
reject(error);
}
};
}
async function test() {
try {
await step(done);
await step(done);
await step(done);
} catch (err) {
console.log(err);
}
}
test();
i have a promise chain
If i receive error in getServiceCost I want to repeat the chain again (retry) for 2 times how can i achieve this when using Promise chain , meaning again execute getUser, getServiceCost
getUser(100)
.then(getServices)
.then(getServiceCost)
.then(console.log);
function getUser(userId) {
return new Promise((resolve, reject) => {
console.log('Get the user from the database.');
setTimeout(() => {
resolve({
userId: userId,
username: 'admin'
});
}, 1000);
})
}
function getServices(user) {
return new Promise((resolve, reject) => {
console.log(`Get the services of ${user.username} from the API.`);
setTimeout(() => {
resolve(['Email', 'VPN', 'CDN']);
}, 3 * 1000);
});
}
function getServiceCost(services) {
return new Promise((resolve, reject) => {
console.log(`Calculate the service cost of ${services}.`);
setTimeout(() => {
resolve(services.length * 100);
}, 2 * 1000);
});
}
If i receive error in getServiceCost I want to repeat the chain again (retry) for 2 times how can i achieve this when using Promise chain , meaning again execute
getUser, getServiceCost
I'd use an async function (all modern environments support them, and you can transpile for obsolete environments), which lets you use a simple loop. Perhaps as a utility function you can reuse:
async function callWithRetry(fn, retries = 3) {
while (retries-- > 0) {
try {
return await fn();
} catch (error) {
if (retries === 0) {
throw error;
}
}
}
return new Error(`Out of retries`); // Probably using an `Error` subclass
}
Using it:
callWithRetry(() => getUser(100).then(getServices).then(getServiceCost))
.then(console.log)
.catch(error => { /*...handle/report error...*/ });
Or
callWithRetry(async () => {
const user = await getUser(100);
const services = await getServices(user);
return await getServiceCost(services);
})
.then(console.log)
.catch(error => { /*...handle/report error...*/ });
im trying to write a promise but seems to be missing something. here is my code:
const myPromise = new Promise(() => {
setTimeout(() => {
console.log("getting here");
return setinputs({ ...inputs, images: imageAsUrl });
}, 100);
});
myPromise
.then(() => {
console.log("getting here too");
firebase.database().ref(`collection/${idNode}`).set(inputs);
})
.then(() => {
console.log("all is set");
})
.catch((err) => {
console.log(err);
});
if i run the program, the first part of the promise is executing but all .then() functions arent executing. how do i fix this?
In this scheme, the promise callback has one (resolve) or two (resolve,reject) arguments.
let p = new Promise((resolve, reject)=> {
//do something
//resolve the promise:
if (result === "ok") {
resolve(3);
}
else {
reject("Something is wrong");
}
});
p.then(res => {
console.log(res); // "3"
}).catch(err => {
console.error(err); //"Something is wrrong
});
Of course, nowadays you can use async + await in a lot of cases.
You need to resolve the promise, using resolve() and also return the promise from firebase so the next .then in the chain works properly.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("getting here");
// You have to call resolve for all `.then` methods to be triggered
resolve({ ...inputs, images: imageAsUrl });
}, 100);
});
myPromise
.then((inputs) => {
console.log("getting here too");
// You have to return a promise in a .then function for the next .then to work properly
return firebase.database().ref(`collection/${idNode}`).set(inputs);
})
.then(() => {
console.log("all is set");
})
.catch((err) => {
console.log(err);
});
I am working with learning more about JavaScript promises and ran into an issue when trying to combine some promise logic out of separate functions into a single inline function. When I pulled it all into a single inline function it is resulting in 'undefined' for the promises return result instead of the intended 'world' value.
[ WORKS as intended, where it resolves out the promise asynchronously, and results in 'world' for the promise response]
app.get('/asyncTest', (request, response) => {
console.log("Request started...");
var helloResult = hello()
.then((res)=> {
console.log('COMPLETED Promise Result (Promise completed): ' + res)
});
console.log('Hello Result (immediately after issuing the promise [still in promise object format]): ' + helloResult);
console.log('Mesage at the end of the request (Should fire before completion of the promise result being fulfilled...');
});
function wait(ms) {
return new Promise(r => setTimeout(r, ms));
}
async function hello() {
await wait(3000);
return 'world';
}
[NOT WORKING - Results in 'undefined' instead of 'world' for the promise response...]
var helloResult = async (r) => {
await new Promise(r => {
setTimeout(r, 3000);
return 'world';
})
};
let helloResponse = helloResult().then((res)=> {
console.log('COMPLETED Promise Result (Promise completed): ' + res)
})
[NOT WORKING - Results in 'undefined' instead of 'world' for the promise response...]
var helloResult = async () => {
await new Promise(r => {
setTimeout(r, 3000);
return 'world';
})
.then((responseData)=> {
console.log('COMPLETED Promise Result (Promise completed): ' + responseData)
})};
For some reason the promise in the second 2 attempts to change the code are resulting out in 'undefined' instead of the expected value from the return result of 'world'.
Any advice is appreciate, thank you for your help.
You are returning from the Promise constructor callback in your latter examples. That value is not resolved. You have to call the resolve callback in order to pass the value along.
await new Promise(r => {
setTimeout(() => r('world'), 3000);
});
You have no return in helloResult() and need to pass argument to resolve()
var helloResult = async(r) => {
return await new Promise(r => {
setTimeout(r, 500, 'world');
})
};
let helloResponse = helloResult().then((res) => {
console.log('COMPLETED Promise Result (Promise completed): ' + res)
})
I see the confusion.
new Promise((resolve, reject) => {
resolve(1);
return 2;
}).then(v => console.assert(v===1))
In the promise constructor you can return a value but it gets ignored. The value passed down the chain is what is passed to the resolve or reject functions. So when you do:
var helloResult = async (r) => {
await new Promise(r => {
setTimeout(r, 3000);
return 'world';
})
};
It's the same concept. setTimeout calls resolve (r) at the right time but doesn't pass the value ('world') in. In your case you have 2 options that both do the same thing essentially:
var helloResult = async (r) =>
await new Promise(r => {
setTimeout(r, 3000, 'world');
});
// or
var helloResult = async (r) =>
await new Promise(r => {
setTimeout(()=> r('world'), 3000);
});
From your second example: then is a method on the Promise API so you need to return a promise from helloResult for that to have any effect. You're also not resolving that promise once the timeout has completed.
var helloResult = () => {
return new Promise(resolve => {
setTimeout(() => resolve('world'), 3000);
});
};
let helloResponse = helloResult().then((res) => {
console.log('COMPLETED Promise Result (Promise completed): ' + res)
})
Alternatively, if you want to use async/await you can, but you need to ensure that code containing the await sits with a corresponding async.
var helloResult = () => {
return new Promise(resolve => {
setTimeout(() => resolve('world'), 3000);
});
};
(async () => {
const response = await helloResult();
console.log(`COMPLETED Promise Result (Promise completed): ${response}`);
})();
You should be able to fix this by using a resolve in your promise, like so:
var helloResult = async (r) => {
await new Promise(r => {
setTimeout(r, 3000);
resolve('world');
})
};
let helloResponse = helloResult().then((res)=> {
console.log('COMPLETED Promise Result (Promise completed): ' + res)
})
Similarly, this can be done for your second block of code where you have chained a then onto the promise.
I have a situation where I think the only choice for me is to nest some Promises within each other. I have a Promise that needs to be performed and a method that does something until that Promise is complete. Something like this:
let promise = new Promise((resolve, reject) => {
// Do some stuff
});
doSomethingUntilPromiseisDone(promise);
However, within my Promise, I need to execute another method that returns another Promise:
let promise = new Promise((resolve, reject) => {
fetchValue(url)
.then((value) => {
// Do something here
}).catch((err) => {
console.error(err);
});
});
doSomethingUntilPromiseisDone(promise);
But now, in the fetchValue method's then statement, I have another method I need to execute that, guess what, returns another Promise:
let promise = new Promise((resolve, reject) => {
fetchValue(url)
.then((value) => {
saveToCache(value)
.then((success) => {
console.log('success!!');
resolve('success');
});
}).catch((err) => {
console.error(err);
});
});
doSomethingUntilPromiseisDone(promise);
So in the end, I have a Promise, within a Promise, within a Promise. Is there someway I can structure this better so that it is more straightforward? It seems like nesting them within each other is counter to Promise's intended chaining approach.
Use .then()
let doStuff = (resolve, reject) => {/* resolve() or reject() */};
let promise = new Promise(doStuff);
doSomethingUntilPromiseisDone(
promise
.then(value => fetchValue(url))
.then(value => value.blob())
.then(saveToCache)
)
.then(success => console.log("success!!"))
.catch(err => console.error(err))
you can use generator to flatten your nested promises (Bluebird.couroutine or Generators)
//Bluebird.couroutine
const generator = Promise.coroutine(function*() {
try {
const value = yield fetchValue(url);
const success = yield saveToCache(value);
console.log('success:', success);
} catch(e) {
console.error(err);
}
}));
generator();
Each function will call the next one with the result of the method before.
var promises = [1,2,3].map((guid)=>{
return (param)=> {
console.log("param", param);
var id = guid;
return new Promise(resolve => {
// resolve in a random amount of time
setTimeout(function () {
resolve(id);
}, (Math.random() * 1.5 | 0) * 1000);
});
}
}).reduce(function (acc, curr, index) {
return acc.then(function (res) {
return curr(res[index-1]).then(function (result) {
console.log("result", result);
res.push(result);
return res;
});
});
}, Promise.resolve([]));
promises.then(console.log);