I have a similar problem in dialogflow fulfillment where I need to book any appointments on the Google calendar. I would reject all functions p1, p2 and p3 if only one of them is rejected. In the code below, although p2 is rejected, the others p1 and p3 are executed (I wish all functions p1, p2 and p3 were not performed).
function f1() {
return Promise.all([p1(), p2(), p3()])
.then(value => {
alert('ok');
})
.catch(err => {
console.log('err: ' + err);
});
}
function p1() {
new Promise((resolve, reject) => {
setTimeout(resolve, 1000, alert("one"));
});
}
function p2() {
new Promise((resolve, reject) => {
reject('reject');
});
}
function p3() {
new Promise((resolve, reject) => {
setTimeout(resolve, 3000, alert("three"));
});
}
f1();
Well it contains a lot of code to implement it so I would give short instructions.
You need to have a way to reject your request or whatever you are doing. For examples with axious we can use CancelToken to cancel HTTP request.
Now you need to subscribe on each request and cancel the request or whatever you are using.
It is not clear what exactly you need. Functions can not be rejected or not executed if you run them in parallel. You can only notify the internal function code that the cancel operation was requested from the outside. We don't know what async operations are performed inside your async functions. Demo
import { CPromise } from "c-promise2";
function makePromise(ms, label) {
return new CPromise((resolve, reject, { onCancel }) => {
setTimeout(resolve, ms);
onCancel(() => console.log(`onCancel(${label})`));
});
}
CPromise.all([
makePromise(2000, "one"),
makePromise(1500, "two").then(() => {
throw Error("Oops");
}),
makePromise(4000, "three")
]).then(
(v) => console.log(`Done: ${v}`),
(e) => console.warn(`Fail: ${e}`)
);
onCancel(one)
onCancel(three)
Fail: Error: Oops
The problem is you're not returning anything from p1, p2, p3. So when you call Promise.all([p1(), p2(), p3()]) which is actually calling with Promise.all([undefined, undefined, undefined])(resolves anyway) which does not have a rejection. That's why you're not seeing the error.
Add return in your functions.
function f1() {
return Promise.all([p1(), p2(), p3()])
.then(value => {
alert('ok');
})
.catch(err => {
console.log('err: ' + err);
});
}
function p1() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
});
}
function p2() {
return new Promise((resolve, reject) => {
reject('reject');
});
}
function p3() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 3000);
});
}
f1();
Remember Promises are not cancellable, if you really want cancel execution part, you can try something like this. I don't guarantee this works all the time and I don't think good idea to do in this way.
const timers = []
function f1() {
return Promise.all([p1(), p2(), p3()])
.then(value => {
alert('ok');
})
.catch(err => {
console.log('err: ' + err);
timers.forEach((timerId) => clearTimeout(timerId))
});
}
function p1() {
return new Promise((resolve, reject) => {
const timerId = setTimeout(() => {
alert(1)
resolve()
}, 1000);
timers.push(timerId)
});
}
function p2() {
return new Promise((resolve, reject) => {
reject('reject');
});
}
function p3() {
return new Promise((resolve, reject) => {
const timerId = setTimeout(() => {
alert(2)
resolve()
}, 3000);
timers.push(timerId)
});
}
f1();
Related
I have a function call like so:
await someFunction(foo);
With someFunction defined like:
const someFunction = foo => {
return new Promise((resolve, reject) => {
if (foo) {
return resolve(someOtherPromise());
}
reject();
}
}
Is this valid code? I have tested it and it seems ok.
Is it correct that await someFunction() will transform to await someOtherPromise() if foo is true i.e. will it continue waiting for someOtherPromise to resolve?
Yes, it is valid code and works by resolving with a promise, but smells like the Promise constructor antipattern. I would instead recommend to write
function someFunction(foo) {
if (foo)
return someOtherPromise();
else
return Promise.reject();
}
or
async function someFunction(foo) {
if (foo)
return someOtherPromise();
else
throw;
}
(and better throw a new Error with a message, not undefined)
I've written some test code to see if it's true:
const someFunction = foo => {
return new Promise((resolve, reject) => {
if (foo) {
return resolve(someOtherPromise(2000));
}
reject();
});
}
const someOtherPromise = async(ms) => {
return new Promise(resolve => setTimeout(() => {
console.log("i'm about to resolve in someOtherPromise");
resolve();
}, ms));
}
const run = async () => {
await someFunction('foo');
console.log('finished waiting for someFunction');
}
run();
I have written 3 function to test it out.
c = () => new Promise((resolve, reject) => {
setTimeout(() => {
console.log('c');
resolve();
}, 1000);
});
b = () => new Promise((resolve, reject) => {
setTimeout(() => {
console.log('b');
resolve(c());
}, 1000);
});
a = async () => {
console.log('a before');
await b();
console.log('a after');
};
a();
As we can see, the output order is a before, b, c then a after. So await had waited for b and continued waiting for c.
I've been fighting with updating the state in Reactjs in the correct order for the past few days which made me realise I need to handle my asynchronous functions properly. Unfortunately, it turns out I don't fully understand Promise() either. I am struggling to make the Promise chain work correctly as my third function is never called in the example below.
componentDidMount() {
this.mockOne()
.then(this.mockTwo())
.then((successMessage) => {
console.log('successMessage: ', successMessage);
this.mockThree()
});
}
mockOne() {
return new Promise((resolve, reject) => {
console.log('mockOne')
})
}
mockTwo() {
return new Promise((resolve, reject) => {
setTimeout(function() {
console.log('mockTwo')
}, 2000)
})
.catch(err => console.log('There was an error in mockTwo:' + err));
}
mockThree() {
return new Promise((resolve, reject) => {
console.log('mockThree')
})
}
console output
I've tried the instructions in the MDC but either mockThree() is called immediately before mockTwo() has a chance to respond or mockThree() isn't called at all.
Any help to get this working will be greatly appreciated.
The answer provided worked perfectly until I tried to chain a few more asynchronous functions. Can anyone help me understand why my first function causes the workflow to pause but the next three functions are completed immediately please?
componentDidMount() {
this.mockOne()
.then(successMessage => {
this.mockTwo();
})
.then(successMessage => {
this.mockThree();
})
.then(successMessage => {
this.mockFour();
});
}
mockOne() {
return new Promise((resolve, reject) => {
console.log("mockOne");
setTimeout(function() {
resolve("Test success message");
}, 2000);
}).catch(err => console.log("There was an error in mockOne:" + err));
}
mockTwo() {
return new Promise((resolve, reject) => {
console.log("mockTwo");
setTimeout(function() {
resolve("Test success message");
}, 2000);
}).catch(err => console.log("There was an error in mockTwo:" + err));
}
mockThree() {
return new Promise((resolve, reject) => {
console.log("mockThree");
setTimeout(function() {
resolve("Test success message");
}, 2000);
}).catch(err => console.log("There was an error in mockThree:" + err));
}
mockFour() {
return new Promise((resolve, reject) => {
console.log("mockFour");
setTimeout(function() {
resolve("Test success message");
}, 2000);
}).catch(err => console.log("There was an error in mockFour:" + err));
}
You have to call the resolve function for the promise to become fulfilled. You also need to make sure you are not invoking this.mockTwo() straight away, but instead just give the function this.mockTwo to then.
class App extends React.Component {
componentDidMount() {
this.mockOne()
.then(this.mockTwo)
.then(successMessage => {
console.log("successMessage: ", successMessage);
this.mockThree();
});
}
mockOne() {
return new Promise((resolve, reject) => {
console.log("mockOne");
resolve();
});
}
mockTwo() {
return new Promise((resolve, reject) => {
console.log("mockTwo");
setTimeout(function() {
resolve("Test success message");
}, 2000);
}).catch(err => console.log("There was an error in mockTwo:" + err));
}
mockThree() {
return new Promise((resolve, reject) => {
console.log("mockThree");
resolve();
});
}
render() {
return null;
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I figured out the answer to my expanded (edited) question of why the chain wasn't working as expected. All I had to do was chain the functions properly, as below:
this.mockOne()
.then(successMessage => {
this.mockTwo()
.then(successMessage => {
this.mockThree()
.then(successMessage => {
this.mockFour()
})
})
})
Basically I am trying to play around to understand more of async/await and promise in JS. I'm trying to make Hello comes in between finished! and third finish!!. So the best guess is, making second then asnyc and await for console.log('Hello'). I've tried both ways below but both are not working as expected.
Approach A
let promise = new Promise((res,rej)=>{
res();
});
promise.then(() => {
console.log('finished!')
}).then(() => {
setTimeout(async function(){
await console.log("Hello"); }, 3000); //async/await at setTimeout level
}).then(() => {
console.log('third finish!!')
})
Approach B:
let promise = new Promise((res,rej)=>{
res();
});
promise.then(() => {
console.log('finished!')
}).then(async () => { //async/await at thenlevel
await setTimeout(function(){
console.log("Hello"); }, 3000);
}).then(() => {
console.log('third finish!!')
})
You need the second section to be a Promise, and return it from the .then so that it's properly chained between the first and the third. setTimeout doesn't return a Promise, you have to explicitly construct a Promise instead:
let promise = new Promise((res,rej)=>{
res();
});
promise.then(() => {
console.log('finished!')
}).then(() => {
return new Promise(resolve => {
setTimeout(function(){
console.log("Hello");
resolve();
}, 1000);
});
}).then(() => {
console.log('third finish!!')
})
Or, using await, use await new Promise followed by the same Promise construction and the setTimeout:
let promise = new Promise((res, rej) => {
res();
});
promise.then(() => {
console.log('finished!')
}).then(async() => {
await new Promise(resolve => {
setTimeout(function() {
console.log("Hello");
resolve();
}, 1000);
});
}).then(() => {
console.log('third finish!!')
})
Another approach is to write an asynchronous setAsyncTimeout function (this hasn't been thoroughly tested, may need tweaks):
async function setAsyncTimeout(callback, interval) {
return new Promise( (resolve, reject) => {
setTimeout(() => {
try {
let inner = callback()
if (inner && inner.then) {
inner.then(resolve, reject)
} else {
resolve(inner)
}
} catch(e) {
reject(e)
}
}, interval)
})
}
Testing via your example:
let promise = new Promise((res,rej)=>{
res();
});
promise.then(() => {
console.log('finished!')
}).then( setAsyncTimeout(function(){
console.log("Hello"); }, 3000);
}).then(() => {
console.log('third finish!!')
})
Testing via your example (with another promise):
let promise = new Promise((res,rej)=>{
res();
});
promise.then(() => {
console.log('finished!')
}).then(async () => { //async/await at thenlevel
await setAsyncTimeout(function(){
console.log("Hello");
return setAsyncTimeout(() => console.log("world"), 3000)
}, 3000);
}).then(() => {
console.log('third finish!!')
})
Cleaner example:
let promise = new Promise((res,rej)=>{
res();
});
promise.then(() => {
console.log('finished!')
}).then(() => {
return setAsyncTimeout(() => { console.log("Hello"); }, 3000)
}).then(() => {
console.log('third finish!!')
})
I'm confused about Promise!
I use Promise then without return like this:
new Promise((resolve, reject) => {
resolve("1");
}).then((v1) => {
console.log("v1");
new Promise((resolve, reject) => {
//Time-consuming operation, for example: get data from database;
setTimeout(() => {
resolve(2)
}, 3000);
}).then((v11) => {
console.log("v11");
})
}).then((v2) => {
console.log("v2")
});
I get this result v1 v2 v11.
Then, I use another way of writing, Like below:
new Promise((resolve, reject) => {
resolve("1");
}).then((v1) => {
console.log("v1");
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 3000)
}).then((v11) => {
console.log("v11");
})
}).then((v2) => {
console.log("v2")
});
I get another result v1 v11 v2.
Maybe, There is another case:
new Promise((resolve, reject) => {
resolve("1");
}).then((v1) => {
console.log("v1");
return new Promise((resolve, reject) => {
setTimeout(() => {resolve(2)}, 3000)
}).then((v11) => {
console.log("v11");
return new Promise((resolve, reject) => {
setTimeout(() => {resolve(2)}, 4000)
}).then(() => {
console.log("v12")
})
})
}).then((v2) => {
console.log("v2")
});
I get this result v1 v11 v12 v2
I can't understand the second return I want to know why I get this result?
It will be easier to understand the control flow if you actually print the values of the resolved promises and not only the names of the variables:
Version 1
new Promise((resolve, reject) => {
resolve("1");
}).then((v1) => {
console.log("v1:", v1);
new Promise((resolve, reject) => {
//Time-consuming operation, for example: get data from database;
setTimeout(() => {
resolve(2)
}, 3000);
}).then((v11) => {
console.log("v11:", v11);
})
}).then((v2) => {
console.log("v2:", v2)
});
Version 2
new Promise((resolve, reject) => {
resolve("1");
}).then((v1) => {
console.log("v1:", v1);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 3000)
}).then((v11) => {
console.log("v11:", v11);
})
}).then((v2) => {
console.log("v2:", v2)
});
Version 3
new Promise((resolve, reject) => {
resolve("1");
}).then((v1) => {
console.log("v1:", v1);
return new Promise((resolve, reject) => {
setTimeout(() => {resolve(2)}, 3000)
}).then((v11) => {
console.log("v11:", v11);
return new Promise((resolve, reject) => {
setTimeout(() => {resolve(2)}, 4000)
}).then((v12) => {
console.log("v12:", v12)
})
})
}).then((v2) => {
console.log("v2:", v2)
});
Now you can see what gets passed to the callbacks:
Result 1
v1: 1
v2: undefined
v11: 2
Result 2
v1: 1
v11: 2
v2: undefined
Result 3
v1: 1
v11: 2
v12: 2
v2: undefined
Explanation
As you can see when in the .then() handlers you don't return a promise, it acts as if you returned an already resolved promise with value undefined - like if you did:
return Promise.resolve(undefined);
and thus the next .then() handler can be called immediately.
If, on the other hand, you return a promise that is not resolved yet, then the next .then() handler will not be invoked immediately but only after that returned promise gets resolved.
And that explains the order of execution that is different when you don't return a promise - and what happens is as if an already resolved promise got returned implicitly for you.
function one() {
return new Promise((resolve,reject) => {
setTimeout(function () {
console.log("one 1 ");
resolve('one one');
}, 2000);
});
}
function two() {
return new Promise((resolve,reject) => {
setTimeout(function () {
console.log("two 2 ");
resolve('two two');
}, 10000);
});
}
function three(){
setTimeout(function () {
console.log("three 3 ");
}, 5000);
}
one().then((msg) => {
console.log('one : ', msg);
return two();
}).then((msg) => {
console.log('two :', msg);
return three();
}).then((msg) => {
console.log('three :', msg);
})
.catch((error) => {
console.error('Something bad happened:', error.toString());
});
console three show undefined because three not parse resolve
Here is my situation:
fetchData(foo).then(result => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result + bar);
}, 0)
});
}).then(result => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve( result + woo);
}, 0)
});
}).then(result => {
setTimeout(() => {
doSomething(result);
}, 0)
});
Where each setTimeout is a different async operation using the callback pattern.
It is really painfull to wrap each function inside a promise, I feel like the code should look more like this:
fetchData(foo).then(result => {
setTimeout(() => {
return result + bar;
}, 0)
}).then(result => {
setTimeout(() => {
return result + woo;
}, 0)
}).then(result => {
setTimeout(() => {
doSomething(result);
}, 0)
});
Obviously this doesn't work.
Am I using Promises right? Do I really have to wrap all existing async function in promises?
EDIT:
Actually I realize my example was not totally reprensentative of my situation, I did not make it clear that the setTimeout in my example is meant to reprensent en async operation. This situation is more representative of my situation:
fetchData(foo).then(result => {
return new Promise((resolve, reject) => {
asyncOperation(result, operationResult => {
resolve(operationResult);
}, 0)
});
}).then(result => {
return new Promise((resolve, reject) => {
otherAsyncOperation(result, otherAsyncResult => {
resolve( otherAsyncResult);
}, 0)
});
}).then(result => {
doSomething(result);
});
Am I using Promises right? Do I really have to wrap all existing async function in promises?
Yes. Yes.
I feel like the code should look more like this
No, it shouldn't. It rather should look like this:
function promiseOperation(result) {
return new Promise((resolve, reject) => {
asyncOperation(result, resolve, 0)
});
}
function otherPromiseOperation(result) {
return new Promise((resolve, reject) => {
otherAsyncOperation(result, resolve, 0)
});
}
fetchData(foo).then(promiseOperation).then(otherPromiseOperation).then(doSomething);
It is really painfull to wrap each function inside a promise
Well, don't repeatedly write it out every time. You can abstract this wrapping into a function!
function promisify(fn) {
return value => new Promise(resolve => {
fn(value, resolve, 0)
});
}
const promiseOperation = promisify(asyncOperation);
const otherPromiseOperation = promisify(otherAsyncOperation);
fetchData(foo).then(promiseOperation).then(otherPromiseOperation).then(doSomething);
Notice that most promise libraries come with a such a promisification function included, so your whole code reduces to these three lines.
You are using promise right. Just a small note on the first snippet of code: you are not returning a promise from the last then() callback:
...
}).then(result => {
setTimeout(() => {
doSomething(result);
}, 0)
});
This is correct if you need simply to do an async operation without returning to the caller of fetchData the value of the last async operation. If you need to return this value, you need to convert to promise this operation too:
fetchData(foo).then(result => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result + bar);
}, 0)
});
}).then(result => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result + woo);
}, 0)
});
}).then(result => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(doSomething(result));
}, 0)
});
});
Here I suppose doSomething is a sync function returning a value.
Said so, if you want to reduce the noise of create the promise to wrap setTimeout every time, you can create a utility function setTimeoutWithPromise:
function setTimeoutWithPromise(operation, millisec) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(operation());
}, millisec)
});
}
And clean your code:
fetchData(foo)
.then(result => setTimeoutWithPromise(() => result + bar, 0))
.then(result => setTimeoutWithPromise(() => result + woo, 0))
.then(result => setTimeoutWithPromise(() => doSomething(result), 0));