I don't understand why it returns each fruit with the same size. It's like it returns only the fruit size of the first fruit / first fetch result. I mean allFruits has always the same size, thats not correct. And make an await in the object element like { name: await getFruitName(f.1)} does not work. Maybe the function should be called in another place?
const food = await fetchFood();
food.entries.map(await fruits)
var allFruits = {}
async function fruits(f) {
allFruits=
{
name: getFruitName(f.1)
}
let fruitSize = await FetchFruitsSize(f.2)
allFruits.size = fruitSize.data[0].sizes[1]
}
return allFruits
If somebody has an example how to change the object value for each array element with calling a function that is calling a fetch, I would be very greateful
Some issues:
allFruits should be an array, and the individual objects that your code creates should become entries in that array, so it becomes an array of objects.
The async fruits callback returns a promise that resolves to undefined, because there is no return statement.
In map(await fruits) it makes no sense to use await for a function object (which is not a promise). Just drop that await keyword.
food.entries.map(fruits) returns an array of promises, but your code does not await those promises. You should feed them to Promise.all and await that combined promise to resolve.
f.1 and f.2 are not valid syntax: if 1 and 2 are supposed to be properties of f, then you need square brackets. But it doesn't look like f is an array, so I have just dropped those syntax issues in below proposed code: adapt as needed.
The above points are resolved in this version:
(async function () {
const food = await fetchFood();
const allFruits = await Promise.all(food.entries.map(fruits));
// Do something with the fruits...
console.log(allFruits);
async function fruits(fruit) {
let fruitSize = await FetchFruitsSize(fruit); // adapt to do f.2
return {
name: getFruitName(fruit), // adapt to do f.1
size: fruitSize.data[0].sizes[1]
};
}
})();
There might still be other issues that relate to what the functions return that are called here... including: fetchFood, FetchFruitsSize, and getFruitName.
Related
I am trying to map object where correct ID will contain the array that belongs to it.
Here is the scenario:
let obj = {}
const arr = []
for (let i = 0; i < someIds.length; i++) {
const res = await someApiCall() // returns array
obj = {
id: someIds[i],
res
}
arr.push(obj)
return arr
}
The thing here is that arr will only output last set of object because of iterating over the values.
For example I get
[{id: 122, res: "whatever122"}]
where I want to get
[{id: 122, res: "whatever122"}, {id: 111, res: "whatever111"}, {id: 133, res: "whatever133}]
How do I concatenate all?
You return arr to fast. It should be returned outside the for scope:
let obj = {}
const arr = []
for (let i = 0; i < someIds.length; i++) {
const res = await someApiCall() // returns array
obj = {
id: someIds[i],
res
}
arr.push(obj)
}
return arr
Move the return statement outside of the for loop, which would wait for all of the objects to get added to the array.
let obj = {}
const arr = []
for (let i = 0; i < someArr.length; i++) {
const res = await someApiCall() // returns array
obj = {
id: res[i],
res
}
arr.push(obj)
}
return arr
The problem is produced by the return statement that should probably stay outside the for loop. It completes the execution of the current function and returns (an optional value) to the caller.
A simpler way to write what you need is to use Promise.all() to run all the calls to someApiCall() in parallel (if they are independent). This way the code run much faster:
async function doSomething() {
return await Promise.all(
someIds.map(
async (id) => {
return {
id: id,
res: await someApiCall(id),
};
}
)
);
}
How it works
someIds.map() calls the callback function that it receives as argument for each item of someIds and returns a new array that contains the values returned by the callback function.
Because the callback function is asynchronous and uses await, it returns a Promise as soon as the await-ed call starts, without waiting it to complete.
The array of promises returned by someIds.map() is passed as argument to Promise.all() that waits for all of them and returns an array containing the values returned by the promises (which are the objects returned by the callback were they be synchronous).
Remark
The await in front of Promise.all() is not really needed. Without it, the function doSomething() returns the promise returned by Promise.all(). With it, the function returns an array of objects. Either way, the call of doSomething() still needs to be await-ed and the value produced by it after await is an array of objects.
I'm learning Promises:
Promise.all([
test1,
test2
]).then((res)=>{
resolve(res);
})
Above code doesn't return the value but a pointer to the function:
[ [Function: test1], [Function: test2] ]
Why is that? The asynchronous nature of JS is difficult to grasp
Full code snippet and further explanation below:
module.exports = function main(dbo, tickers, quotes) {
const arr = tickers.split(',');
let underlyingPrice = 0;
let collection = '';
let obj = {};
arr.forEach((ticker) => {
if (quotes[ticker].assetType === 'EQUITY') {
Object.assign(obj, {
[ticker]: {}
})
underlyingPrice = quotes[ticker].regularMarketLastPrice;
collection = dbo.collection(`priceHistory-${ticker}`);
getfastMA(underlyingPrice, collection);
getslowMA(underlyingPrice, collection);
}
})
Promise.all([
getfastMA,
getslowMA
]).then((res)=>{
console.log(res);
})
}
What I want to happen:
arr.forEach loops through an array, grabs ticker and passes onto getfastMA and getslowMA functions. those functions execute mongodb query that contains data for that specific ticker. Also, those functions calculate averages as Promise
the two functions are Promise which I want to resolve together once the loop above has exited
Save the averages into an obj.
What I actually get is:
[ [Function: getfastMA], [Function: getslowMA] ]
I saw several YouTube videos, that I thought I was duplicating the syntax but obviously I'm not
Probably test1 is and test2 are your promise returning functions and you do not call them to return promises to hand in to Promise.all as you try to do. Promise.all requires an array of promises as one argument. So one needs to call these functions somewhere and pass the promises in an array to .all method. Following example calls them inline.
Promise.all([
test1(),
test2()
]).then((res)=>{
resolve(res);
})
With your post update use your get* named functions instead. However, again, do not forget to call them for example like my above code so that you have the promises they return and so that the Promise.all can work with that. Then it will return you what each promise returns if they all settle fulfilled.
I want to refactor a promise chain into async/await, but Typescript is complaining about the typing.
TS2322:Type 'IHttpPromiseCallbackArg< IResp >' is not assignable to type 'IResp'...
I thought await would return a regular value, not a promise. Am I wrong? If so, how can I assign a typing so that the desired code will compile?
I thought await would return the same value as the first argument in the .then callback. Am I wrong?
Old code:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
Desired new code:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const resp:IResp = await this.myAsyncRequest(params); //typing error with "const resp:IResp"
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
The desired code still breaks if I remove the return type in myAsyncRequest ; I guess Typescript infers directly from the AngularJS library.
myAsyncRequest(params:IParams):IHttpPromise<IResp> {
return $http.post('blah', data);
}
If I remove "IResp" from the const resp declaration, processResponse complains that IHttp< IResp> does not equal IResp...
processResp(resp:IResp) {
//do stuff
}
Your question "I thought await would return the same value as the first argument in the .then callback. Am I wrong?".
No, you are absolutely right. But you are wrong about what the first argument in the .then callback is.
You define myAsyncRequest to return IHttpPromise<IResp>. But IHttpPromise<T> is defined as inheriting IPromise the following way:
type IHttpPromise<T> = IPromise<IHttpPromiseCallbackArg<T>>;
So, an IHttpPromise<T> is a promise that returns an IHttpPromiseCallbackArg<T> back where the actual data of type T is in the data property of the IHttpPromiseCallbackArg<T>.
So, the old code variant we see in the question:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
should actually not compile without errors in TypeScript when myAsyncRequest is defined to return IHttpPromise.
Howto fix this:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const httpResp:IHttpPromiseCallbackArg<IResp> = await this.myAsyncRequest(params);
const resp: IResp = httpResp.data;
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
Note: In the latest type definitions for angular, the type IHttpPromiseCallbackArg<T> is actually called IHttpResponse<T>.
Maybe in your code, you have defined IResp as IHttpResponse<Something>? Then you just have a conflict with the old name IHttpPromiseCallbackArg. Then get the newest type definitions from DefinitelyTyped that uses the new name. And you would also have to change the definition of myAsyncRequest to:
myAsyncRequest(params:IParams):IHttpPromise<Something> {
The line containing the await does indeed await the resolved value - but because the function itself is async (to allow all that awaiting), you get back a promise.
Example... in the below code you can use x as a plain number (even though delay returns a promise) and again for y - so all the stuff you await is resolved so you can use it more like you would if it were synchronous.
The async function that doesn't look like it returns a promise, now does.
This can seem confusing, because it seems to "invert the promises", but what it does is shift the then to the top level (you could have async functions calling down to other async functions and so on).
function delay(ms: number) {
return new Promise<number>(function(resolve) {
setTimeout(() => {
resolve(5);
}, ms);
});
}
async function asyncAwait() {
let x = await delay(1000);
console.log(x);
let y = await delay(1000);
console.log(y);
return 'Done';
}
asyncAwait().then((result) => console.log(result));
var promiseReturningFuncs = [];
for(var i = 0; i < 5; i++){
promiseReturningFuncs.push(askQuestion);
}
var programmers = [];
Promise.reduce(promiseReturningFuncs, function(resp, x) {
console.log(typeof resp);
if(typeof resp != "function") {
programmers.push(resp);
}
return x();
})
.then(function(resp) {
programmers.push(resp);
console.log(programmers);
});
My goal: execute the askQuestion function in series and resolve an array of objects created by that function. (this function must execute in series so that it can respond to user input)
So imagine that the askQuestion function returns a promise that resolves a object I want to add to an array.
This is my messy way of doing it.
I am looking to find a cleaner way of doing it, ideally, i wouldn't even need to push to an array, I would just have a final .then, where the response is an array.
Since you appear to be using the Bluebird promise library, you have a number of built-in options for sequencing your promise returning functions. You can use Promise.reduce(), Promise.map() with a concurrency value of 1, Promise.mapSeries or Promise.each(). If the iterator function returns a promise, all of these will wait for the next iteration until that promise resolves. Which to use depends more upon the mechanics of how your data is structured and what result you want (neither of which you actually show or describe).
Let's suppose you have an array of promise returning functions and you want to call them one at a time, waiting for the one to resolve before calling the next one. If you want all the results, then I'd suggest Promise.mapSeries():
let arrayOfPromiseReturningFunctions = [...];
// call all the promise returning functions in the array, one at a time
// wait for one to resolve before calling the next
Promise.mapSeries(arrayOfPromiseReturningFunctions, function(fn) {
return fn();
}).then(function(results) {
// results is an array of resolved results from all the promises
}).catch(function(err) {
// process error here
});
Promise.reduce() could also be used, but it would accumulate a single result, passing it from one to the next and end with one final result (like Array.prototype.reduce() does).
Promise.map() is a more general version of Promise.mapSeries() that lets you control the concurrency number (the number of async operations in flight at the same time).
Promise.each() will also sequence your functions, but does not accumulate a result. It assumes you either don't have a result or you are accumulating the result out-of-band or via side effects. I tend to not like to use Promise.each() because I don't like side effect programming.
You could solve this in pure JS using ES6 (ES2015) features:
function processArray(arr, fn) {
return arr.reduce(
(p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
Promise.resolve([])
);
}
It applies the function given to the array in series and resolves to an array of the results.
Usage:
const numbers = [0, 4, 20, 100];
const multiplyBy3 = (x) => new Promise(res => res(x * 3));
// Prints [ 0, 12, 60, 300 ]
processArray(numbers, multiplyBy3).then(console.log);
You'll want to double check browser compatibility but this works on reasonably current Chrome (v59), NodeJS (v8.1.2) and probably most others.
You can use recursion so that you can move to the next iteration in a then block.
function promiseToExecuteAllInOrder(promiseReturningFunctions /* array of functions */) {
var resolvedValues = [];
return new Promise(function(resolve, reject) {
function executeNextFunction() {
var nextFunction = promiseReturningFunctions.pop();
if(nextFunction) {
nextFunction().then(function(result) {
resolvedValues.push(result);
executeNextFunction();
});
} else {
resolve(resolvedValues);
}
}
executeNextFunction();
}
}
Executing one after another using a recursive function( in a non promise way):
(function iterate(i,result,callback){
if( i>5 ) callback(result);askQuestion().then(res=>iterate(i+1,result.concat([res]),callback);
})(0,[],console.log);
For shure this can be wrapped in a promise:
function askFive(){
return new Promise(function(callback){
(function iterate(i,result){
if( i>5 ) callback(result);askQuestion().then(res=>iterate(i+1,result.concat([res]),callback);
})(0,[],console.log);
});
}
askFive().then(console.log);
Or:
function afteranother(i,promise){
return new Promise(function(resolve){
if(!i) return resolve([]);
afteranother(i-1,promise).then(val=>promise().then(val2=>resolve(val.concat([val2])));
});
}
afteranother(5,askQuestion).then(console.log);
I'm getting more and more confortable with the asyncawait library.
If I'm correct, this:
var doSth = async function() {
var asyncResult = await promiseReturningFunction();
subFunction(asyncResult);
};
is equivalent to:
var doSth = function() {
promiseReturningFunction().then(function(asyncResult) {
subFunction(asyncResult);
});
};
But what if the callback has two arguments .then(function(asyncResult1, asyncResult2) {})?
In some other languages, I'd write:
var asyncResult1, asyncResult2 = await promiseReturningFunction();
but I can't do this in JS, can I? Does await returns an array? The first argument?
I can't do this in JS, can I?
No, you can't. Promises fulfill with only a single value.
JavaScript doesn't have tuples, but you can use an array for that. With destructuring, you can make it look almost like in those other languages:
async function promiseReturningFunction() {
await …;
…
return [result1, result2];
}
// elsewhere (in an async function):
var [asyncResult1, asyncResult2] = await promiseReturningFunction();
You can easily use the destructuring on a bunch of promises like that:
var [r1, r2, r3] = await Promise.all([p1, p2, p3]);
or
var promises = [p1(), p2(), p3()];
$q.all(promises)
.then(function ([p1Result, p2Result, p3Result]) {
// ...
});
So you will have:
async function doSth() {
var [asyncResult1, asyncResult1] = await promiseReturningFunction();
var asyncLastResult = await subFunction(asyncResult1);
return asyncLastResult;
}
doSth();
Promises can only pass one argument to the fulfillment callback. As Benjamin Gruenbaum writes:
[...] Just the first parameter will be treated as resolution value in the promise constructor. You can resolve with a composite value like an object or array.
(Here is the section in the Promises/A+ specification)
So your case with two arguments doesn't occur in JavaScript (at least not with standard promise implementations). Just use an array or an object (depending on your data structure).