Spinner while executing nested async functions (JS Vanilla) [duplicate] - javascript

I am having trouble with a function returning an Array of undefined.
Here is code:
classMethods.getQueries = function(models, dbId, dateStart, dateEnd) {
return new Promise(function(resolve, reject) {
// Fetch database.
.then(extractQueries, reject)
.then(sortQueries, reject)
.then(onlyTen, reject)
.then(addText, reject)
.then(function(queries) {
console.log("getQueries finished", queries); // Array of 10× undefined!
resolve(queries);
}, reject);
// Functions here.
});
};
Everything is fine until the addText function:
function addText(queries) {
return Promise.all(queries.map(function(query) {
models.queries.findById(query.queryId, {
raw: true,
attributes: [ "query" ]
})
.then(function(queryFetched) {
query.text = queryFetched.query;
console.log(query);
return Promise.resolve(query);
}, function(error) {
return Promise.reject(error);
});
}));
};
This is giving me an output like:
"getQueries finished" [ undefined ×10 ]
10×
[query database]
{ queryId: "…", text: "…" }
I have no idea why the promise is returned while the loop is not finished.

Promise.all accepts an Array of Promise objects.
You’re getting an Array of undefined because you’re not returning any Promise in your map callback:
function addText(queries) {
return Promise.all(queries.map(function(query) {
// Add `return` here or the `map` returns an Array of `undefined`.
return models.queries
.findById(query.queryId, {
raw: true,
attributes: [ "query" ]
})
.then(function(queryFetched) {
query.text = queryFetched.query;
console.log(query);
return Promise.resolve(query);
}, function(error) {
return Promise.reject(error);
});
}));
};
A primitive value such as undefined will resolve immediately in Promise.all, therefore it resolves before any of the Promises in your callback resolve.

So here the solution of my problem:
function addText(queries) {
return Promise.all(queries.map(function(query) {
return new Promise(function(resolve, reject) {
models.queries.findById(query.queryId, { raw: true, attributes: ['query'] })
.then(function(queryFetched) {
query.text = queryFetched.query;
resolve(query);
}, reject);
});
}));
};

I was having the same problem, my Promise.all(list), results in an array of undefined, I used the same approach of adding a return to my function.

Related

Javascript Promise undefined

I am new to Javascript and AngularJS, I am trying to figure this out.
I made an angular service that execute DB queries and return promise.
executeStatement = function(db, sql, values, onsuccess, onerror) {
if (!!db.executeSql) {
return db.executeSql(sql, values || [], onsuccess, onerror);
} else {
return db.transaction(function(tx) {
return tx.executeSql(sql, values, function(ignored, rs) {
return onsuccess(rs);
}, function(ignored, error) {
return onerror(error);
});
});
}
};
this.executeStatement = function(sql, values) {
$ionicPlatform.ready( function() {
return new Promise(function(resolve, reject) {
return executeStatement(myDB, sql, values, resolve, reject);
});
} );
}
And then If I call the executeStatement like this in the angular service module.
this.extract = function(callback) {
_DB.executeStatement('SELECT * FROM FRIDGE', []).then(callback);}
Shouldn't this return a promise after the functions are finished running?
I'm getting undefined :(
Some help would be appreciated!
The this.executeStatement() function needs to return the result of $ionicPlatform.ready():
this.executeStatement = function(sql, values) {
return $ionicPlatform.ready( function() {
return new Promise(function(resolve, reject) {
return executeStatement(myDB, sql, values, resolve, reject);
});
} );
}
Now you can access whatever's returned from executeStatement in your callback, which you can pass in as the argument to the .then() function:
_DB.executeStatement('SELECT * FROM FRIDGE', []).then(callback);
The functions within executeStatement are not Promise, do not need to be returned, where onFulfilled, onRejected of Promise constructor is passed as parameters to a function, if and when onsuccess, onerror are called, the original Promise will be resolved or rejected.
Include second parameter to chained .then() or use .catch() to handle potential errors within Promise chain.
Not sure if $ionicPlatform.ready returns a Promise?
_executeStatement = function(db, sql, values, onsuccess, onerror) {
if (!!db.executeSql) {
// `onsuccess` or `onerror` need to be called for `Promise`
// to be fullfiled; a value needs to be passed to
// `onsuccess` or `onerror` to get the value at `.then()` or `.catch()`
db.executeSql(sql, values || [], onsuccess, onerror);
} else {
db.transaction(function(tx) {
tx.executeSql(sql, values, function(ignored, rs) {
onsuccess(rs);
}, function(ignored, error) {
onerror(error);
});
});
}
};
this.executeStatement = function(sql, values) {
return $ionicPlatform.ready( function() {
return new Promise(function(resolve, reject) {
_executeStatement(myDB, sql, values, resolve, reject);
})
.catch(function(err) {
// handle, pass error here
console.log(err);
return err
})
});
}
this.executeStatement(/* parameters */)
.then(function success(data) {
console.log(data);
}, function err(err) {
console.log(err);
});

All functions in Q.all block are not being promised

I have the following code below in a then block
The issue I'm facing is at the end when i do the res.json(optionData1) its not returning the fully completed js data object i.e. the output after the processData function is missing
Am i using Q.all in the correct way?
var processUserInfo = function(categoryToProcess, inputToProcess, optionComingIn) {
var d = Q.defer();
if (optionData1['option'] == optionComingIn) {
if (optionData1[categoryToProcess].hasOwnProperty(inputToProcess)) {
optionData1[categoryToProcess][inputToProcess]++;
} else {
optionData1[categoryToProcess][inputToProcess] = 1;
}
d.resolve(optionData1);
}
}
var processData = function(item, optionComingIn) {
var d = Q.defer();
return User.find(
{_id: item},
{gender: 1, country:1},
function(req, foundUser) {
processUserInfo('gender', foundUser[0]['gender'], optionComingIn)
.then(function(resolve,reject) {
d.resolve();
});
});
return d.promise;
}
Q.all(foundQ[0]['people'].map(function(item) { // Or Q.allSettled
processCounts(item['optionSelected']);
processData(item['userID'], item['optionSelected']);
}))
.then(function(){
res.json(optionData1); //Doesnt give me the full result
});
Thanks
UPDATE: Using the return method as in the answer below got everything working.
Here is code which may work - too much "unknown" in your code snippet to be sure
modified processData to return a promise that resolves when user.Find is done
added a return in the .map, so the promise returned by processData is waited on in Q.all
So ... here's the fixed code (processuserInfo unchanged so omitted form the answer)
var processData = function (item, optionComingIn) {
// return a promise to wait for
return Q.promise(function(resolve, reject) {
User.find({
_id: item
}, {
gender: 1,
country: 1
},
function (req, foundUser) {
processUserInfo('gender', foundUser[0]['gender'], optionComingIn);
resolve();
}
);
});
}
Q.all(foundQ[0]['people'].map(function (item) { // Or Q.allSettled
processCounts(item['optionSelected']);
return processData(item['userID'], item['optionSelected']);
// return added
}))
.then(function () {
res.json(optionData1); //Doesnt give me the full result
});

Promise.all is returning an array of undefined and resolves before being done

I am having trouble with a function returning an Array of undefined.
Here is code:
classMethods.getQueries = function(models, dbId, dateStart, dateEnd) {
return new Promise(function(resolve, reject) {
// Fetch database.
.then(extractQueries, reject)
.then(sortQueries, reject)
.then(onlyTen, reject)
.then(addText, reject)
.then(function(queries) {
console.log("getQueries finished", queries); // Array of 10× undefined!
resolve(queries);
}, reject);
// Functions here.
});
};
Everything is fine until the addText function:
function addText(queries) {
return Promise.all(queries.map(function(query) {
models.queries.findById(query.queryId, {
raw: true,
attributes: [ "query" ]
})
.then(function(queryFetched) {
query.text = queryFetched.query;
console.log(query);
return Promise.resolve(query);
}, function(error) {
return Promise.reject(error);
});
}));
};
This is giving me an output like:
"getQueries finished" [ undefined ×10 ]
10×
[query database]
{ queryId: "…", text: "…" }
I have no idea why the promise is returned while the loop is not finished.
Promise.all accepts an Array of Promise objects.
You’re getting an Array of undefined because you’re not returning any Promise in your map callback:
function addText(queries) {
return Promise.all(queries.map(function(query) {
// Add `return` here or the `map` returns an Array of `undefined`.
return models.queries
.findById(query.queryId, {
raw: true,
attributes: [ "query" ]
})
.then(function(queryFetched) {
query.text = queryFetched.query;
console.log(query);
return Promise.resolve(query);
}, function(error) {
return Promise.reject(error);
});
}));
};
A primitive value such as undefined will resolve immediately in Promise.all, therefore it resolves before any of the Promises in your callback resolve.
So here the solution of my problem:
function addText(queries) {
return Promise.all(queries.map(function(query) {
return new Promise(function(resolve, reject) {
models.queries.findById(query.queryId, { raw: true, attributes: ['query'] })
.then(function(queryFetched) {
query.text = queryFetched.query;
resolve(query);
}, reject);
});
}));
};
I was having the same problem, my Promise.all(list), results in an array of undefined, I used the same approach of adding a return to my function.

Conditional on Promise.all [duplicate]

This question already has answers here:
Wait until all promises complete even if some rejected
(20 answers)
Closed 7 years ago.
I'm aware of this answer and the following code is based on it. It is working but I'm not happy with something.
What if I want to make some conditional checking and resolve based on it? I've commented on code where I'd like to change.
/**
* Returns not yet propagate subdomains.
* #return Promise
*/
Tour.prototype.getOffline = function() {
var self = this;
var resolve_, reject_;
// to be fulfilled later
var promise = new Promise(function(resolve, reject){
resolve_ = resolve;
reject_ = reject;
});
var check = function(key) {
return new Promise(function(resolve, reject) {
redis_client.hmget(key, 'online', function(err, reply) {
if (reply[0] === 'n') {
resolve(key); // <----- I'd like to resolve only here
} else {
// reject(); // <---- I can't reject cause of Promise.all
resolve(); // <----- An empty value to satisfy Promise.all
}
});
});
};
this.getKeysRedis('subdomain:*').then(function(resp) {
var promises = resp.map(check);
var results = Promise.all(promises);
results.then(function(data) {
// I have undefined values on data array
var array = data.filter(function(key){
return utils.isDefAndNotNull(key);
});
resolve_(array); // <------ How can I resolve without filtering
});
}, function(error) {
reject_(error);
});
return promise;
};
Final code, removed Promise constructor antipattern as #Bergi alerted:
Tour.prototype.getOffline = function() {
var self = this;
var check = function(key) {
return new Promise(function(resolve, reject) {
redis_client.hmget(key, 'online', function(err, reply) {
if (reply[0] === 'n') resolve(key);
else reject();
});
});
};
var reflect = function(promise) {
return promise.then(
x => ({state: 'resolved', value: x}),
e => ({state: 'rejected' , value: e})
);
};
return new Promise(function(resolve, reject) {
self.getKeysRedis(self.subdomain_keyname + '*')
.then(function(keys) {
return Promise.all(keys.map(check).map(reflect)).then(function(r) {
return r.filter(x => x.state === 'resolved');
});
})
.then(function(results) {
var array = results.map((result) => result.value);
resolve(array);
})
.catch((err) => reject(err));
});
};
You basically want a function that takes a promise and returns a promise that fulfills whenever the other promise resolves (fulfills or rejects):
That function is typically called "reflect":
function reflect(promise){
return promise.then(x => ({state: "fulfilled", value: x}),
e => ({state: "rejected" , value: e}));
}
This means you can reject (like you normally would) and then do:
this.getKeysRedis('subdomain:*').then(resp =>
resp.map(check).map(reflect); // note the reflect
).then(Promise.all).then(results => {
// access results here
results[0].state; // "fulfilled" if it fulfilled
results[0].value; // the value or error
});

Promise.allSettled in babel ES6 implementation

I'm using babel to transpile my node.js#0.10.x code and I'm stuck with promises.
I need allSettled-type functionality that I could use in q and bluebird or angular.$q for example.
On babel's core-js Promise, there is no allSettled method.
Currently I'm using q.allSettled as a workaround:
import { allSettled } from 'q';
Is there something like that in babel polyfill? Alternatively, which is a good algorithm for me to try to implement?
2019 Answer
There was a proposal to add this function to the ECMAScript standard, and it has been accepted! Check out the Promise.allSettled docs for details.
Original Answer
If you take a look at the implementation of q.allSettled you'll see it's actually quite simple to implement. Here's how you might implement it using ES6 Promises:
function allSettled(promises) {
let wrappedPromises = promises.map(p => Promise.resolve(p)
.then(
val => ({ status: 'fulfilled', value: val }),
err => ({ status: 'rejected', reason: err })));
return Promise.all(wrappedPromises);
}
2020 answer:
What the other answers are trying to do is to implement Promise.allSettled themselves. This was already done by the core-js project.
What you need to do is to make babel polyfill Promise.allSettled for you via core-js. The way you configure it to do so is through #babel/preset-env like so:
presets: [
['#babel/preset-env', {
useBuiltIns: 'usage',
corejs: {version: 3, proposals: true},
}],
],
In your build artifact this will add a call to require("core-js/modules/esnext.promise.all-settled") which monkeypatches the .allSettled function to the promises API.
const allSettled = promises =>
Promise.all(promises.map(promise => promise
.then(value => ({ state: 'fulfilled', value }))
.catch(reason => ({ state: 'rejected', reason }))
));
Or if you insist on polyfilling it:
if (Promise && !Promise.allSettled) {
Promise.allSettled = function (promises) {
return Promise.all(promises.map(function (promise) {
return promise.then(function (value) {
return { state: 'fulfilled', value: value };
}).catch(function (reason) {
return { state: 'rejected', reason: reason };
});
}));
};
}
Taken from here
Alternatively, which is a good algorithm for me to try to implement?
create a new promise with an executor function
use a counter/result array in the scope of the executor
register a then() callback with each parent promise saving the results in the array
resolve/reject promise from step 1 when counter indicates that all parent promises are done
Here's my attempt at something similar, I have Newsletter service and in my case I wanted my allSettled promise to resolve with an array of all the results (rejections and resolutions), IN ORDER, once all the email_promises are settled (all the emails had gone out):
Newsletter.prototype.allSettled = function(email_promises) {
var allSettledPromise = new Promise(function(resolve, reject) {
// Keep Count
var counter = email_promises.length;
// Keep Individual Results in Order
var settlements = [];
settlements[counter - 1] = undefined;
function checkResolve() {
counter--;
if (counter == 0) {
resolve(settlements);
}
}
function recordResolution(index, data) {
settlements[index] = {
success: true,
data: data
};
checkResolve();
}
function recordRejection(index, error) {
settlements[index] = {
success: false,
error: error
};
checkResolve();
}
// Attach to all promises in array
email_promises.forEach(function(email_promise, index) {
email_promise.then(recordResolution.bind(null, index))
.catch(recordRejection.bind(null, index));
});
});
return allSettledPromise;
}
my implementation will be below
Promise.prototype.myAllSettled = function (arr = []) {
return new Promise(function processIterable(resolve, reject) {
let result = [];
arr.forEach((item) => {
item
.then((value) => {
result.push({ status: "fulfilled", value: value });
if (arr.length === result.length) resolve(result);
})
.catch((err) => {
result.push({ status: "rejected", reason: `${err}` });
if (arr.length === result.length) resolve(result);
});
});
});
};
Here's another take at the same functionality: spex.batch
The source code would be too much to re-post here, so here's just an example from the batch processing of how to use it:
var spex = require('spex')(Promise);
// function that returns a promise;
function getWord() {
return Promise.resolve("World");
}
// function that returns a value;
function getExcl() {
return '!';
}
// function that returns another function;
function nested() {
return getExcl;
}
var values = [
123,
"Hello",
getWord,
Promise.resolve(nested)
];
spex.batch(values)
.then(function (data) {
console.log("DATA:", data);
}, function (reason) {
console.log("REASON:", reason);
});
This outputs:
DATA: [ 123, 'Hello', 'World', '!' ]
Now let's make it fail by changing getWord to this:
function getWord() {
return Promise.reject("World");
}
Now the output is:
REASON: [ { success: true, result: 123 },
{ success: true, result: 'Hello' },
{ success: false, result: 'World' },
{ success: true, result: '!' } ]
i.e. the entire array is settled, reporting index-bound results.
And if instead of reporting the entire reason we call getErrors():
console.log("REASON:", reason.getErrors());
then the output will be:
REASON: [ 'World' ]
This is just to simplify quick access to the list of errors that occurred.

Categories