I've been reading everything I can about async/await and Promises, but I can't quite get it to work. I keep getting 'Promise ' and when it's fulfilled I receive 'Undefined'.
I think I've spent about two days on this! I'm not sure what's wrong. Here's the un-promisified version.
const getCoords = (address) => {
geocoder.addressSearch(address, (result) => {
return result;
}
);
};
And here is my attempt at the promised code:
const getCoords = async (address) => {
await geocoder.addressSearch(address, async (result) => {
return result;
}
);
};
I'm just trying to return the result. If anyone could steer me in the right direction it would be really appreciated.
Both of your examples are incorrect
using continuation-passing style
In this style, the continuation ("callback") ignores any return values as this function is run asynchronously from the main call -
const geocoder =
{ addressSearch: (address, callback) =>
setTimeout(callback, 1000, {
coords: [12.345, 67.890]
})
}
const getCoords = (address, callback) =>
geocoder.addressSearch(address, result =>
callback(result.coords)
)
getCoords("foobar", console.log)
// [ 12.345, 67.89]
[ 12.345, 67.89]
using promises
In this style you do not specify a continuation. Instead a Promise is returned which represents the value of a future computation. You can await a promise in an async function in order to retrieve the result, or chain a .then handler -
const geocoder =
{ addressSearch: (address, callback) =>
setTimeout(callback, 1000, {
coords: [12.345, 67.890]
})
}
const getCoords = (address) =>
new Promise(resolve =>
geocoder.addressSearch(address, result =>
resolve(result.coords)
)
)
async function main() {
const coords = await getCoords("foobar")
console.log(coords)
return "done"
}
main().then(console.log, console.error)
[ 12.345, 67.89]
"done"
Related
I'm trying to curry a function but when the first one is async it throws the error function1(...) is not a function, however if I pass the async function as the last one it works fine.
Can anyone tell me why this is happening? and how to properly make a curry function that starts with a async function?
Thanks to anyone who take the time.
//This one is throwing the error: function1(...) is not a function
async function function1(path) {
const fetchedElement = await fetchElement(path);
//(...)
return (msg) => {
console.log(msg);
};
}
function1('somepath.html')('my message');
//This one works fine, properly returning other function
function function2(path) {
return async (msg) => {
const fetchedElement = await fetchElement(path);
//(...)
console.log(msg);
};
}
function2('somepath.html')('my message');
It depends on when the async work needs to get done. If you want the currying process to do the async work, then you can't invoke the currying function synchronously:
curryingFunction(paramA)(paramB)
^ assumes curryingFunction is synchronous
Instead, use two statements by the caller, one to await the currying, the other to invoke...
const fetch = path => new Promise(resolve => setTimeout(() => {
console.log('fetched ' + path)
resolve()
}, 1000));
async function curryingFunction(path) {
const fetchedElement = await fetch(path);
return message => {
console.log(message);
}
}
async function someCaller() {
// await to get a curried function, then invoke it
let curried = await curryingFunction('some_path');
curried('some message');
}
someCaller()
On the other hand, you might not need to do the async work in order to get the curried function. You probably don't. In that case, you can make the currying function synchronous, but have it return an async function that does the async work.
As a result, you'll get to use the fn()() syntax that you're probably used to using...
const fetch = path => new Promise(resolve => setTimeout(() => {
console.log('fetched ' + path)
resolve()
}, 1000));
function curryingFunction(path) {
return async (message) => {
const fetchedElement = await fetch(path);
console.log(message);
}
}
async function someCaller() {
// here we can use the fn()() syntax that we're accustomed to
await curryingFunction('some path')('some message')
}
someCaller()
As the first function is async You need to await it first, then call the second parameter.
//This one is throwing the error: function1(...) is not a function
async function function1(path) {
const fetchedElement = await Promise.resolve(1);
//(...)
return (msg) => {
console.log(msg);
};
}
(async () => {
try {
(await function1('somepath.html'))('my message');
} catch(e) {
console.log(e)
}
})()
I am trying to understand how promises work in JS by playing with swapi.dev. I would like to create a dynamic chain of promises (not using async/await) but it does not provide me with any result. In particular, the idea behind is to get all names of the given person (for instance Luke Skywalker) and dump them into the console.
Could anyone help me? What am I missing?
Thanks in advance.
"use strict";
const request = require("request-promise");
const BASE_URL = "http://swapi.dev/api";
var currentPromise = Promise.resolve();
callApiPromise(`${BASE_URL}/people/1`).then((data) => {
console.log("Getting vehicles' URLs");
const vehicles_URL = data["vehicles"];
console.log("Starting looping through URLs");
for (let i = 0; i < vehicles_URL.length; i++) {
console.log(`i=${i}, vehicle_URL=${vehicles_URL[i]}`);
currentPromise = currentPromise.then(function () {
console.log(".. getting vehicle name");
return getVehicleName[vehicles_URL[i]];
});
}
});
function getVehicleName(url) {
callApiPromise(url).then((vehicle_data) => {
var arrVehicleData = new Array();
arrVehicleData.push(vehicle_data);
console.log(arrVehicleData.map((vehicle) => vehicle.name));
});
}
function callApiPromise(url) {
return new Promise((resolve, reject) => {
callApi(url, (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
});
});
}
function callApi(url, callback) {
request
.get(url)
.then((response) => {
const json = JSON.parse(response);
callback(null, json);
})
.catch((err) => {
callback(err, null);
});
}
Some issues:
A missing return statement in getVehicleName
A syntax issue in getVehicleName[vehicles_URL[i]] (should be parentheses)
As the promises for getting the vehicle names are independent, you would not chain them, but use Promise.all
arrVehicleData will always only have one element. There is no reason for an array there where it is used.
You are also taking the wrong approach in using request.get. The bottom function turns that API from a Promise-API to a callback API, only to do the reverse (from callback to promise) in the function just above it. You should just skip the callback layer and stick to promises:
"use strict";
const request = require("request-promise");
const BASE_URL = "http://swapi.dev/api";
getJson(`${BASE_URL}/people/1`).then(data => {
return Promise.all(data.vehicles.map(getVehicleName));
}).then(vehicleNames => {
console.log(vehicleNames);
// Continue here...
});
function getVehicleName(url) {
return getJson(url).then(vehicle => vehicle.name);
}
function getJson(url, callback) {
return request.get(url).then(JSON.parse);
}
Finally, you should not use request-promise anymore since the request module, on which request-promise depends, has been deprecated
The getVehicleName doesn't return a promise. Instead it invokes a promise that by the time it will be resolved, the for loop invoking it will already be removed from the call stack.
This is a sample of promise chaining:
const promise = new Promise(resolve => resolve(1))
const promise1 = Promise.resolve(2)
const methodReturnPromise = () => new Promise(resolve => resolve(3))
promise.then(firstPromiseData => {
// do something with firstPromiseData
console.log(firstPromiseData)
return promise1
}).then(secondPromiseData => {
// do something with secondPromiseData
console.log(secondPromiseData)
return methodReturnPromise()
}).then(thirdPromiseData => {
// do something with thirdPromiseData
console.log(thirdPromiseData)
})
I have a collection of promises or async functions, I need to manipulate slightly and then execute in parallel.
The issue I am having is that I am unable to resolve the promises.
// Some dummy tasks:
const taskA = () => Promise.resolve(500);
const taskB = () => {
return new Promise(resolve => resolve(300));
};
// Push them into an array:
const tasks = [];
const registerTask = (name, task) => {
tasks.push( async () => {
return { [name]: await task() };
});
};
// trying to get the results
const runTasks = () => {
const result = Promise.all(tasks).then(results => results);
return result;
}
// usage
registerTask('taskA', taskA);
registerTask('taskB', taskB);
console.log(runTasks())
Following the successful resolution of promises ideally I would also like to handle errors individually for each task.
The problem is that your registerTask function pushes functions onto the tasks array, not Promise objects. If you change the function like this, it should work:
const registerTask = (name, task) => {
const asyncFunc = async () => {
return { [name]: await task() };
};
tasks.push( asyncFunc() ); // This pushes a promise into 'tasks'
};
Your original code ended up with tasks being an array of functions that have not been called yet. When you call Promise.all(tasks) it ended immediately with undefined for each entry in the array because there were no Promise objects to wait on.
As for error handling, the Promise.all will fail with the first rejection or exception that happens. If you want to handle each task's individual error case, then you should do that in the Promise created in registerTask since doing it during the Promise.all is too late. It's hard to give an example since I'm not sure what kind of error handling would be appropriate for tasks, but perhaps something like this:
const registerTask = (name, task) => {
const asyncFunc = async () => {
return { [name]: await task() };
};
const promise = asyncFunc()
.catch(err => {
return handleError(name, err);
});
tasks.push( promise );
};
function handleError(name, err) {
console.log(`Task ${name} had error ${err}`);
// don't re-throw the error if you want all tasks to complete
// return some error object so you can see which tasks failed after they're done
return {error: err, name: name};
}
Promise.all expects an array of promises and you are passing an array of functions that returns a promise. So change your promise Promise.all to receive the promise of each function:
// Some dummy tasks:
const taskA = () => Promise.resolve(500);
const taskB = () => {
return new Promise(resolve => resolve(300));
};
// Push them into an array:
const tasks = [];
const registerTask = (name, task) => {
tasks.push( async () => {
return { [name]: await task() };
});
};
// trying to get the results
const runTasks = () => {
const result = Promise.all(tasks.map(task => task())); // Execute the tasks to receive the promises
return result;
}
// usage
registerTask('taskA', taskA);
registerTask('taskB', taskB);
runTasks().then((res) => {console.log(res)})
In my react app, I have a function that somewhat simulates a call to an API. Is there a way I can synchronously run a callback function after this API request so that my data comes back in the order I sent it?
// the order in the array here matters
const wordTypeArr = ['type1', 'type2', 'type3']
// loop sequentially through array
wordTypeArr.forEach((v,i) => {
getRandomWordFromAPI(v, addWord)
})
//simulated "API" - can't modify this function
const getRandomWordFromAPI = (type, callback) => {
return setTimeout(function() {
callback(
type in dictionary ?
sample(dictionary[type]) :
null
);
}, (Math.random() * 750 + 250));
}
//callback runs after response - update the state
const addWord = (val) => {
const newState = wordList
newState.push(val)
setWordList(newState);
}
As you can see, the getRandomWordFromAPI function returns a timeout function then executes the callback after the timeout asynchronously (out of order). This is undesirable, as my results must be in order.
Maybe I need to wrap addWord in a promise? or something similar?
Changing that function would indeed be preferred as it'll make the code simpler to read. But since you can send getRandomWordFromAPI a callback, you can send the resolve function as the callback instead of addWord() and then chain the addWord to the resolution.
I've put some parts of your code in comments, since we don't have the dictionary object and such, but the structure stays the same.
// the order in the array here matters
const wordTypeArr = ['type1', 'type2', 'type3'];
//simulated "API" - can't modify this function
const getRandomWordFromAPI = (type, callback) => {
return setTimeout(function() {
callback( type );
/*
callback(
type in dictionary ?
sample(dictionary[type]) :
null
);*/
}, (Math.random() * 750 + 250));
}
//callback runs after response - update the state
const addWord = (val) => {
console.log( val );
/*
const newState = wordList
newState.push(val)
setWordList(newState);
*/
}
const randomWords = wordTypeArr.map( word => {
return new Promise(( resolve, reject ) => {
getRandomWordFromAPI( word, resolve );
});
});
Promise.all( randomWords ).then( words => words.forEach( addWord ));
It's 2019, promises are in, callbacks are out. On a more serious note, here's how you can refactor your code to make it work the way you want to:
// mock the API
const getRandomWordFromAPI = (type, callback) => {
setTimeout(() => {
let word = `some-word-of-type-${type}`;
callback(word);
}, 1000)
}
// promisify the mocked API
const getRandomWordFromAPIPromise = (type) => new Promise(resolve => {
getRandomWordFromAPI(type, resolve);
});
// fetch all data asynchronously, in order
const wordTypeArr = ['type1', 'type2', 'type3'];
const promises = wordTypeArr.map(getRandomWordFromAPIPromise);
Promise.all(promises).then(words => {
// do whatever you want with the words
console.log(words)
});
I'm new in React and I was looking to achieve this kind of flow:
// set the state
// execute a function `f` (an async one, which returns a promise)
// set the state again
// return the promise value from the previous function
So, what I'm doing now is the following:
async function handleSomething() {
this.setState((prevState) => { ... },
() => {
let result = await f()
this.setState((prevState) => { ... },
...
)
})
return result;
}
Hope you get the idea of what I want to achieve. Basically I want to get result, which is the value returned from awaiting f, and return it in handleSomething so I can use it in another place, but wrapping it up inside those setState calls:
// g()
// setState
// res = f()
// setState
// return res
My question is, how can I do this properly? Maybe should I modify the state with the result value and get it from there?.
EDIT:
Usage of handleSomething:
// inside some async function
let result = await handleSomething()
You can create a Promise that resolves once both setState calls are done:
function handleSomething() {
return new Promise(resolve => {
this.setState(
prevState => {
/*...*/
},
async () => {
let result = await f();
this.setState(
prevState => {
/*...*/
},
() => resolve(result)
// ^^^^^^^ resolve the promise with the final result
);
}
);
});
}
Which would be used like:
this.handleSomething().then(result => /* ... */)
// or
const result = await this.handleSomething();