I am working on a Vue.js project where I am trying to run a series of promises depening on each other. To make this example simple I have cut out all but one and replaced the rest with a console.log which should output the value I am trying to access to then use later on. If I can get this example to work then the rest is just repeating it.
createBuilding: function() {
return new Promise((resolve, reject) => {
if(this.building === 'New building') {
this.$store.dispatch('newBuilding', {
address: this.address,
number_units: this.number_units
})
.catch(err => {
reject(err)
})
resolve(this.$store.getters.buildingID)
} else {
resolve(this.building)
}
})
},
onComplete: async function() {
let buildingID = await this.createBuilding()
console.log(buildingID)
alert('Success');
},
Real results:
I see the console.log fire off with undefined, then the alert then the awaited promise/function shows in the vue dev tools.
How can I get this so that I can get the result of the createBuilding method to use with my other methods?
This is promise constructor antipattern. There's no need to use new Promise in case a promise already exists and can be chained. The antipattern leaves the space for mistake, which is the case here.
newBuilding is expected to be asynchronous, but the promise is resolved instantly, this results in race condition.
It should be:
createBuilding() {
if(this.building === 'New building') {
return this.$store.dispatch('newBuilding',...)
.then(() => this.$store.getters.buildingID)
} else {
return Promise.resolve(this.building)
}
},
With async..await, it's simplified to:
async createBuilding() {
if(this.building === 'New building') {
await this.$store.dispatch('newBuilding',...);
return this.$store.getters.buildingID
} else {
return this.building
}
},
Related
Hello I am a new coder and I saw this Electron helper to prompt for a value via input. https://github.com/p-sam/electron-prompt. I was wondering how I would store the value from user input. This is the code I have but I don't really understand how to pull out the data(user input) from the code to use. I would appreciate any help, thank you!
async function getStoreId()
{
prompt({
title: 'Get StoreId',
label: 'Store ID: ',
value: '',
inputAttrs: {
type: 'guid'
},
type: 'input'
})
.then((r) => {
if(r === null) {
console.log('user cancelled');
} else {
console.log('result', r);
//storeid = r;
}
})
.catch(console.error);
}
let storeid = await getStoreId;
console.log(storeid);
The prompt() function in the electron-prompt library returns a promise.
A promise can only be in one of three states:
Pending
Fulfilled, or
Rejected
Quite often, due to the sequential nature of Javascript, if you try and console.log() a promise before it has been fulfilled, you will receive a Promise { <pending> } message.
As you have wrapped your own function getStoreId() around the prompt() function (which returns a promise), you will not return anything unless you place a return statement in front of the prompt() function.
As a result, your getStoreId() function will now return the promise.
To handle the returned promise you must follow it up with a .then() method. The .then() method will then be processed when the promise is resolved (IE: It is no longer in the 'pending' state), meaning it has either been fulfilled or 'rejected'.
Inside the .then() method you can extract the result and either process it then and there or pass it on to another functions of yours for clearer, cleaner, more easily readable code.
function getStoreId() { // Removed async keyword
return prompt({ // Added return statement
title: 'Get StoreId',
label: 'Store ID:',
value: '',
inputAttrs: {type: 'guid'},
type: 'input'
})
.then((result) => {
if (result === null) {
console.log('user cancelled');
} else {
return result; // Return the result
}
})
// PS: Don't forget to handle any caught errors gracefully.
.catch(console.error);
}
// Called only when your getStoreId() function has resolved.
function useResult(result) {
// Do something useful with your result.
console.log(result);
}
// Calling "getStoreId()" without a ".then()" method will only
// return a "Promise { <pending> }" message at this point in the code.
// console.log(getStoreId());
// Let's show the prompt window.
getStoreId()
.then((result) => { useResult(result); })
It's not much I discovered Javascript Promise.
However I found out a behaviour I couldn't understand concerning the nesting of (new or returned) Promise inside Promises
That's the background (extPromiseX is a third-party function returning Promise):
Case 1:
function myAction(param) {
return extPromise1(sql).then((result) => {
[...]
return extPromise2(sql2).then((result) => {
[...]
return extPromise3(sql3);
})
});
}
// Main Process
myAction(...)
.then(() => {
console.log('Process Terminated');
}).catch((error) => {
console.error('Exit with error', error);
});
Now, as expected, I got from the console, in
1) extPromise1 completed
2) extPromise2 completed
3) extPromise3 completed
4) Process Terminated
Case 2:
function myAction(param) {
return new Promise(function() {
if (itsAllOkWithInputs) {
// Some sync code here
return extPromise1(sql).then((result) => {
[...]
return extPromise2(sql2).then((result) => {
[...]
return extPromise3(sql3);
})
})
} else {
throw 'Something went wrong';
}
});
}
// Main process
myAction(...)
.then(() => {
console.log('Process Terminated');
}).catch((error) => {
console.error('3) --> Exit with error', error);
})
In this second case extPromises are executed but the very first Promise remains pending (confirmed by debug). So the console shows:
1) extPromise1 completed
2) extPromise2 completed
3) extPromise3 completed
I empirically realized that I had to change myAction function as follow for the code to work:
function myAction(param) {
return new Promise(function(resolve, reject) {
if (itsAllOkWithInputs) {
// Some sync code here
let ep = extPromise1(sql).then(...);
resolve(ep);
} else {
throw 'Something went wrong';
}
});
}
My question:
I thought returning a promise inside another parental one would make
the parent resolving with the results of the child. This is the case
inside the then code block applied to the external promises. Why
this is not valid for the new Promise case?
Because .then is meant to chain promises. You can return a Promise from inside the then callback, and then itself will return a new Promise.
The Promise constructor is supposed to construct a Promise from an underlying callback. If you return a Promise from inside a Promise constructor, you are doing something wrong conceptually. And that's why it does not work.
function myAction(param) {
if (itsAllOkWithInputs) {
return extPromise1(sql).then(...);
} else {
return Promise.reject('Something went wrong');
}
}
// OR
async function myAction(param) {
if (itsAllOkWithInputs) {
await extPromise1(sql);
} else {
throw 'Something went wrong';
}
}
new Promise(function() {
You are not using neither resolve nor reject arguments (you actually even haven't declared them), so the promise will never get resolved or rejected. All other stuff is irrelevant.
If your function returns promise, you have to simply call it and return a result. If you don't know whether the function return promise or just a value, you can wrap it's call into Promise.resolve, but not in new Promise.
I have a system that wants to create many folders in dropbox using the api, however i appear to attempt to create all the folders at once which causes errors to be generated, stating that i am performing too many write operations from dropbox.
My code is as follows and first uses reduce to create multiple promises which i thought were chained. In these promises the function add is called, which uploads the case to mongodb and then creates a dropbox folder for it, however this results in throttling errors..
bulkAdd: function (req, callback) {
issues = []
i = 1
req.reduce((promise, audit) => {
return promise.then(_ => this.add(audit, function(err,data){
if (err){
console.log('\n'+i+ ' ' + data.scanner_ui + '\n');
}
}));
}, Promise.resolve()).catch(error => {console.log(error)});
},
add: function (req, callback) {
delete req.status
var audit = new Audit(req);
if (req['status_value'] != undefined && req.status_value != ''){
console.log(req['status_value'])
audit.status = [{
status_value : req['status_value'],
status_notes : req['status_notes'],
status_date : req['status_date'],
}]
}
audit.save(function (err, data) {
if (err) {
callback(err, data)
}
else {
return dropbox_functions.createFolder(data.ui)
.then(response => {console.log(response)}, error=> {console.log('\n\n\n',error.error.error)})
.catch(error => console.log(error))
}
});
},
So the problem in your current question comes from the fact that your add function doesn't return a value, I only see it returning undefined.
If a promise returns something else than a promise in its then / catch block, it will use this input for the following function, and it will not wait for any internal processes to run through before finishing
If inside your then / catch blocks, you would return a promise, it would wait before continuing to the next then block, thus handling your requests sequential.
Now in your current code, I believe the easiest would be to handle the resolve inside your reduce, since you seem to be stuck with your callback handle already.
req.reduce((promise, audit) => {
return promise.then(_ => new Promise(
function( resolve, reject) {
this.add(audit, function(err,data){
if (err){
console.log('\n'+i+ ' ' + data.scanner_ui + '\n');
reject( err );
return;
}
resolve( data );
});
})
);
}, Promise.resolve()).catch(error => {console.log(error)});
in this case, the promise would either reject or resolve itself. I have chosen to submit err and data respectively, so this would be handled in the end in case an error occurs, and so that you can have the last data that got saved successfully
To properly chain you should make sure that you return a promise object.
Your reduce in the end creates a promise chain something like this.
Promise.resolve()
.then(() => {
console.log('setting up first timeout');
setTimeout(() => {
console.log("1 wait");
}, 2000);
})
.then(() => {
console.log('setting up second timeout');
setTimeout(() => {
console.log("2 wait");
}, 2000);
})
.catch(err => console.log("error", err));
If you run it, you'll see that it does not wait for one promise to end before moving down the chain.
Whereas, in the second example it waits for the first one to complete because a Promise object is returned by the first promise.
Promise.resolve()
.then(() => {
return new Promise(function(resolve, reject) {
console.log('setting up first timeout');
setTimeout(() => {
console.log("1 wait");
resolve();
}, 2000);
});
})
.then(() => {
return new Promise(function(resolve, reject) {
console.log('setting up second timeout');
setTimeout(() => {
console.log("2 wait");
resolve();
}, 2000);
});
})
.catch(err => console.log("error", err));
The difference is the first example is not returning Promise object. So, if you make sure each success handler returns a Promise object which is resolved only after your add function is done executing, you should be fine.
Unless there is a good reason for bulkAdd() to be written in nodeback style, you will find it more convenient to return a promise. The need for a nodeback will disappear and .reduce() will sit much more comfortably inside the function.
Using .reduce() and preserving nodeback style is possible but cumbersone, as it involves a rather ugly double-shuffle from nodeback to promise, and promise back to nodeback.
Assuming you are free to adapt the caller(s) to accept a returned promise, the code would be something like this :
'bulkAdd': function(req) {
return req.reduce((promise, audit) => {
return promise.then(_ => this.add(audit));
}, Promise.resolve());
},
'add': function(req) {
delete req.status;
var audit = new Audit(req);
if (req.status_value != undefined && req.status_value != '') {
audit.status = [{
'status_value': req.status_value,
'status_notes': req.status_notes,
'status_date': req.status_date
}];
}
return new Promise((resolve, reject) => { // in-line promisification of audit.save()
audit.save((err, data) => {
err ? reject(err) : resolve(data);
});
})
.then(data => dropbox_functions.createFolder(data.ui));
},
all catching and logging intentionally removed
The detail may differ, depending primarily on :
what data (if any) you want to be delivered to the caller
what you want to happen when errors occur.
This question already has answers here:
Wait until all promises complete even if some rejected
(20 answers)
Closed 5 years ago.
I am using API call to get data and update my redux store. Following is state changes for each API call.
Before making API call, set isLoading as true
On success reset isLoading as false
On failure reset isLoading as false and set isError as true.
My component needs data from three APIs which is stored in three different redux stores as below structure.
store: {
book: {
isLoading: false,
isError: false,
data: {}
},
teacher: {
isLoading: false,
isError: false,
data: {}
},
}
In my component, I use following to call api
componentWillMount() {
const {
loadBook,
loadTeacher,
} = this.props;
// all these load functions dispatch action which returns Promise for async API call using `fetch`
const apiCalls = [
loadBook(),
loadTeacher(),
];
Promise.all(apiCalls);
}
I have written selector to check the loading state as below.
export const getIsLoading = createSelector([
getIsBookLoading,
getIsTeacherLoading,
],
(bLoading, tLoading) => (
bLoading || tLoading
)
);
Based on value of getIsLoading I do show loading state in my component otherwise render component.
However I see problem happens when one of the API call fails. For example, loadBook fails in 100 ms and that time bLoading is changed back to false however tLoading still is true bcz loadTeacher api calls was not finished. Since Promise.all() do either all or nothing therefore API call for loadTeacher never finished therefore tLoading stas true.
Is there a way to let Promsie.all to resolve all the calls even if some failed so that it can clean dirty state?
If loadbook fails then loadTeacher won't stop, your resolve handler of Promise.all simply isn't called.
You can see in the following code that both tLoading (set false in resolve) and bLoading (set in catch) are false:
var bLoading = true;
var tLoading = true;
const loadBook =
() => Promise.reject("load book rejects");
const loadTeacher =
() => Promise.resolve("load book rejects")
.then(()=>tLoading=false);
Promise.all([
loadBook()
.catch(
(err)=>{
bLoading=false;
return Promise.reject(err);
}
),
loadTeacher()
])
.catch(
()=>console.log("bLoading:",bLoading,"tLoading:",tLoading)
);
Cannot insert as snipped because that is buggy as hell in Stack Overflow and keeps failing but you can run the code in the console.
Ash Kander is on the right track if you want to use Promise.all without failing but how do you distinguish between valid resolve values and rejects? It's better to make an explicit fail value:
const Fail = function(reason){this.reason=reason;};
Promise.all(
[
loadBook,
loadTeacher
].map(
fn=>
fn()
.catch(
(err)=>
new Fail(err)
)
)
)//this will never fail
.then(
([book,teacher])=>{
console.log("did book fail?",(book&&book.constructor===Fail));
console.log("did teacher fail?",(teacher&&teacher.constructor===Fail));
}
)
You have to either use a dedicated library (there are some, I dont remember the names -_-) or do it yourself - which is not so hard.
I do have some code doing it:
var MyPromise = {};
MyPromise.getDefaultPromise = function () {
return new Promise(function (resolve, reject) { resolve(); });
};
MyPromise.when = function (promises) {
var myPromises = [];
for (var i = 0; i < promises.length; i++) {
myPromises.push(MyPromise.reflect(promises[i]));
}
return Promise.all(myPromises).
then(function (oResult) {
var failure = oResult.filter(function (x) { return x.status === 'error';});
if (failure.length) {
return MyPromise.fail(failure);
}
return MyPromise.getDefaultPromise();
});
};
MyPromise.fail = function (failure) {
return new Promise(function (resolve, reject) {
reject(failure);
});
};
MyPromise.reflect = function (promise) {
return new Promise(function (resolve) {
promise.then(function (result) {
resolve(result);
}).
catch(function (error) {
resolve(error);
});
});
};
Calling MyPromise.when(-your promises array-) will ALWAYS resolve, and send you an array containing the failing promises that you can analyze in the 'then'
I'm writing unit-tests for my function that fetches info from some REST API. I am using ramda Future type (source).
The following test works weird:
it('should return Maybe of Nothing', done => {
let response = {
status: 200,
json: () => {
return {
results: []
}
}
}
let fakeFetch = {
fetch: () => {
return new Promise((resolve, reject) => {
resolve(response)
})
}
}
// String -> Future Error Maybe
let result = Utils.fetchGiantBomb(faker.random.word(), fakeFetch.fetch);
result.fork(err => {
assert.fail(err, 'expected to return Maybe of Nothing');
done();
}, data => {
expect(Maybe.isJust(data)).to.be.true;
done();
})
})
data should be of type Maybe.Nothing. If I expect Maybe.isNothing the test passes, but I want to see what happens when the test fails, so I set it to Maybe.isJust, which return false. After looking at this for a while, I noticed that when the expect fail it jump up to the error handling (err callback), which then just stop executing any assertion (which result in a 2000ms timeout).
In the Future sources I saw that when the success callback fails, it executes the failure callback. How can I complete this test so it display that the data is not what I expect?
I think the problem is that, when your REST call fails, done() is never called.
Not sure if expect has a .catch method when it fails, but you can try to add
.catch(done);
at the end of your expect function.
Hope that helps.
Calling future.fork(errorHandler, successHandler) will currently ensure that any exceptions thrown in the successHandler will propagate through to the errorHandler.
One way around this (though perhaps not ideal as it is undocumented) is to call future._fork(errorHandler, successHandler) rather than future.fork where errors thrown in the successHandler will not be captured.
Alternatively, a number of test frameworks support passing an error to the done callback such as:
result.fork(err => {
done('Expected to return Maybe of Nothing: ' + err);
}, data => {
expect(Maybe.isJust(data)).to.be.true;
done();
})
I think Ramda shouldn't catch the exception there. But i don't know what's they are trying to do.
It look like you are using Mocha. It maybe good idea to convert your Future to Promise first, then observe the Promise. ie:
const futureToPromise = future => {
return new Promise((resolve, reject) => future.fork(reject, resolve))
}
it('should return Maybe of Nothing', () => {
let response = {
status: 200,
json: () => {
return {
results: []
}
}
}
let fakeFetch = {
fetch: () => {
return new Promise((resolve, reject) => {
resolve(response)
})
}
}
// String -> Future Error Maybe
let result = Utils.fetchGiantBomb(faker.random.word(), fakeFetch.fetch);
// return it because Mocha can handle this
return futureToPromise(result).then(data => {
expect(Maybe.isJust(data)).to.be.true;
}, () => {
// fail
assert.fail(err, 'expected to return Maybe of Nothing');
})
})