Converting a synchronous function to be async - javascript

As I was implementing service worker subscriptions, I noticed a message from Visual Studio Code, that the following function can be converted to be an async function:
Here is the my original synchronous code.
/**
* Returns the subscription if it is present, or nothing
*/
function getUserSubscription() {
return navigator.serviceWorker.ready
.then(function (serviceWorker) {
return serviceWorker.pushManager.getSubscription();
})
.then(function (pushSubscription) {
return pushSubscription;
});
}
I'm not sure how I'd convert this to be an async function. I thought I understood the gist of it, as in converting something like this:
fetchData()
.then(process())
.then(processAgain());
to this:
const response1 = await fetchData();
const response2 = await process(response1);
const response = await processAgain(response2);
But I've had no luck converting my function using this technique.

Use the async keyword in front of the function. Then await each Promise and return the result.
async function getUserSubscription() {
const serviceWorker = await navigator.serviceWorker.ready;
const pushSubscription = await serviceWorker.pushManager.getSubscription();
return pushSubscription;
}

Related

How to stream data using promises?

How can I stream data using promises.
I have two different functions in two different files. In one calls an API service and that returns a promise.
async myPromise(){
return new Promise((resolve, reject) => {
callToAnAPI().then(()=>{
resolve ("pending");
}).then(()=>{
resolve(callToAnotherAPI());
})
.catch(err=>{
// error handling
});
});
}
In another file I have a function like so:
async myPromise2(){
functionFromOtherFile().then((data)=>{
// how can I get 'pending' here?
}).then(data =>{
// how can I get data fromncallToAnotherAPI() here?
})
}
I want to know that the api has been called and that it is in 'pending' state. How can I achieve this?
Stream is just an async iterator... So we could just use callback, much like node.js
function myPromise(cl) {
cl(null, "pending")
setTimeout(() => {
cl(null, "data")
}, 2000);
}
function myPromise2() {
myPromise((err, data) => {
console.log(data)
})
}
myPromise2()
The most elegant way in my opinion is to return two promises and process them separately.
function myPromise(){
const api1Status = callToAnAPI().then(()=>{
resolve ("pending");
});
return [
api1status,
api1status.then(()=>{
resolve(callToAnotherAPI());
})
.catch(err=>{
// error handling
})
];
}
Then the second file would use it like this:
async myPromise2(){
const [api1, api2] = functionFromOtherFile();
const shouldSayPending = await api1;
const shoudHaveData = await api2;
}
The first function doesn't need to be an async one then, you just return a number of promises.
You could also consider async generators, which would give you a nicer code in the first method, but less nice in the second, like this:
async function* myPromise() {
try {
yield await callToAnApi(); // we need to await here so that
yield callToAnotherApi(); // this method is executed after.
} catch(e) {
// error handling
}
}
The other side would result in something like this:
async myPromise2() {
const progress = theIteratorFromOtherFile(); // just call the function*
const shouldBePending = (await progress.next()).value;
const theOtherResult = (await progress.next()).value;
}
Performance wise there's very little difference between the two - you're doing async operations so these are your bottlenecks. The choice is then up to your personal preference.

Asynchronous function call from synchronous function (Node.js)

I am having trouble with getting access to a response from my asynchronous function. I understand this is a common issue, but I cannot find another question that pertains to my issue.
My Synchronous Function:
const output = getSeasons.getSeasons('*URL*', (data)=>{
return data
})
console.log(data)
My Asynchronous Function:
const getSeasons = async (url, callback) => {
seasonTitle = *ASYNCHRONOUS CALL THAT RETURNS AN ARRAY*
await seasonTitle
callback(seasonTitle)
}
My issue is that I want to be able to continue in my synchronous function utilizing "output" in future lines. The only solution I can think of is:
const output = getSeasons.getSeasons('*URL*', (data)=>{
return data
}).then(()=>{ console.log(data) }
My issue with this option is that all of my future code for this function will have to be written inside the ".then" function. I would much prefer to wait for a response before continuing on in my synchronous function without putting everything in ".then". Mainly because I will have multiple functions calling asynchronous functions and that will result in multiple nested ".then" functions.
EDIT:
Here is the actual code, the asynchronous call is scraping for elements with a class of seasons and then I am trying to return the array:
const output = getSeasons.getSeasons('https://www.rocketleagueesports.com/schedule/', (data)=>{
console.log(data)
})
console.log(output)
const getSeasons = async (url, callback) => {
const browser = await puppeteer.launch({ headless: true })
const page = await browser.newPage()
await page.goto(url)
await page.waitForSelector('.match') //Waits for elements with class "match" to load before executing further
const seasonTitle = page.evaluate(() => {
const seasonTitleArray = Array.from(document.querySelectorAll('.sub-header .scoreboard-container .container-lg .show-on-md .select-options-container .seasons li'))
return seasonTitleArray.map(li => li.textContent)
})
await seasonTitle
callback(seasonTitle)
}
Why not use the await as you have done in getSeasons
const getSeasons = async funciton(url) {
return await *ASYNCHRONOUS CALL THAT RETURNS AN ARRAY*
}
(async () => {
const output = await getSeasons.getSeasons('*URL*')
console.log(output)
})()
async/await
If you don't want to write code in then block, then use async/await
async someFunction() {
const output = await getSeasons.getSeasons(...)
}
Besides that, your getSessions need some improvements. Why do you need both callback and async which return a Promise?
Try to refactor something like this:
const getSeasons = async function(url) {
return *ASYNCHRONOUS CALL THAT RETURNS AN ARRAY*
}

Why doesn't the catch in async await code fire?

is there any .catch() method like there is with Promises for async await style of code?
Here's an example of a code written via Promise:
const apiURL = 'https://jsonplaceholder.typicode.com/todos/1';
const badURL = 'zhttps://wcaf.fajfkajf.gg'
function getData(url){
fetch(url)
.then(response => response.json())
.then(json => console.log(json))
.catch( err => console.log('cannot load api'))
}
getData(apiURL);
getData(badURL);
A simple function to try to load data and if not, display a basic error message. Now I was trying to transcribe this into async/await style code, issue was, I could not really figure out a way to write this with catch()
My best guess was to try try - catch but the catch part doesn't work:
const apiURL = 'https://jsonplaceholder.typicode.com/todos/1';
const badURL = 'zhttps://wcaf.fajfkajf.gg'
async function getData(url){
const response = await fetch(url);
try {
const json = await response.json();
console.log(json);
} catch (e) {
console.log('cannot load api');
}
}
getData(apiURL);
getData(badURL);
This loads the object API just fine, but never seems to go into the catch{} block despite being passed incorrect url.
Any idea what am I doing wrong?
As pointed out in the comments by #l-portet, this is because the code inside try { } block does not actually fail!
.json() will return a promise, regardless of the content of the parsed body text, so even though the initial fetch() fails, you can still call .json() on it - albeit it's completely redundant as it won't return anything meaningful.
Putting the fetch() request inside the try { } block does result in the expected behaviour:
const apiURL = 'https://jsonplaceholder.typicode.com/todos/1';
const badURL = 'zhttps://wcaf.fajfkajf.gg'
async function getData(url){
try {
const response = await fetch(url);
const json = await response.json();
console.log(json);
} catch (e) {
console.log('cannot load api');
}
}
getData(apiURL);
getData(badURL);
One thing you should be aware is that when an async function is executed, it always returns a promise, regardless the exit condition of the function.
If the function has an explicit return (or completes without crashing) the promise will be resolved to the value it returned (or to undefined if there was no explicit return), if the function throws, the promise will be rejected, passing the thrown error object.
Knowing that you could simply handle the error where you use the function, for example:
const apiURL = 'https://jsonplaceholder.typicode.com/todos/1';
const badURL = 'zhttps://wcaf.fajfkajf.gg'
async function getData(url){
const response = await fetch(url);
return await response.json();
}
getData(apiURL).then(data => console.log(data));
getData(badURL).catch(err => console.log('error:', err));
IMHO handling the error closely where you have a use-case of the function makes more sense, since normally when you expect to have an error is because we have a way to handle it (maybe try another API url in this example).
One pattern that I've been using lately is to wrap promises in a way they resolve returning a tuple, in the convention of [error, value] (similar to the way the Go programming language handle async error), in that way for instance you could handle the error in the specific getData call, for example:
const apiURL = 'https://jsonplaceholder.typicode.com/todos/1';
const badURL = 'zhttps://wcaf.fajfkajf.gg'
async function getData(url){
const response = await fetch(url);
return await response.json();
}
// simple utility function
const safePromise = promise =>
promise.then(data => [null, data]).catch(err => [err, undefined]);
(async () => {
const [err, json] = await safePromise(getData(apiURL))
if (err) {
// handle the error
}
console.log(json)
const [error, data] = await safePromise(getData(badURL))
if (error) {
console.log('Error:', error);
}
})()
Check the following library which basically ships this pattern:
await-to-js

How to make `return` respecting async/await in JS?

I'm trying to make this code (a class method) returning a String.
async sign() {
const txt = 'ddddd';
const result = await crypto.createHash('md5').update(txt, 'binary').digest('hex');
return result;
}
The problem is that it ignores await and returns a Promise. This function's returned value is used as a HTTP request header, and while npmlog says it's
apisign: Promise { 'faaa3f1409977cbcd4ac50b5f7cd81ec' }
in network traffic caught by Wireshark I see
apisign: [object Promise]
How do I make return respecting await, or how should I write it so it returns a String?
You should not return the value of an async function as is, since it is a Promise
await for it before serializing it.
An async function always returns a Promise.
If you invoke sign() inside another function then you have to await for it and this requires making the caller function also an async function and so on and so forth.
Eventually, at the top-level code you have to use the regular .then().catch() syntax to wait for the Promise to settle:
sign()
.then((result) => {
// do something with the result (e.g. put it into the response header)
console.log(result);
})
.catch((err) => {
// something wrong happened and the Promise was rejected
// handle the error
console.log(`There was an error: ${err.message || err}`);
});
You will have to await the response of a async function.
const getURL = (title, page) => `https://jsonmock.hackerrank.com/api/movies/search/?Title=${title}&page=${page}`
const callService = async (title, page) => {
let response = await fetch(getURL(title, page));
return await response.json();
}
async function callApi() {
let data = await callService('spiderman', 1);
console.log(data.data.length);
}
callApi();

When using async await, how do you specify a callback?

I was looking at how to use transactions in:
https://node-postgres.com/features/transactions
But in the following code example:
const { Pool } = require('pg')
const pool = new Pool()
(async () => {
// note: we don't try/catch this because if connecting throws an exception
// we don't need to dispose of the client (it will be undefined)
const client = await pool.connect()
try {
await client.query('BEGIN')
const { rows } = await client.query('INSERT INTO users(name) VALUES($1) RETURNING id', ['brianc'])
const insertPhotoText = 'INSERT INTO photos(user_id, photo_url) VALUES ($1, $2)'
const insertPhotoValues = [res.rows[0].id, 's3.bucket.foo']
await client.query(insertPhotoText, insertPhotoValues)
await client.query('COMMIT')
} catch (e) {
await client.query('ROLLBACK')
throw e
} finally {
client.release()
}
})().catch(e => console.error(e.stack))
It seems that the function will execute immediately. Also there doesn't seem to be a way to specify a callback. Would it make sense to place the entire block from "(async()...." into a function, and then in the final statement before the end of the try block, add :
await callbackfunction();
Does that make sense? What would be a better way to add a callback function ?
The point of await is that you don't use a callback. It returns the result of resolving the promise.
Without await:
do_something_asyc.then(function (data) { alert(data); });
With await:
var data = await do_something_asyc();
alert(data);

Categories

Resources