How to handle specific errors code in a geolocation promise? - javascript

I have a function that fetches the user's location. It was working this way:
const fetchGeoLocation: SearchService["fetchGeoLocation"] = async () => {
const geo = navigator.geolocation;
if (!geo) throw new Error("error.geolocation-unavailable");
const handleError = (err: any) => {
if (err.code === 1) throw new Error("error.geolocation-permission_denied");
if (err.code === 2) throw new Error("error.geolocation-unavailable");
if (err.code === 3) throw new Error("error.geolocation-timeout");
};
const handleSuccess = (position) => {
return { location: [position.coords.longitude, position.coords.latitude] };
};
geo.getCurrentPosition(handleSuccess, handleError, { maximumAge: 10000 });
};
const onUpdateLocation = async () => {
onLoad();
fetchGeoLocation()
.then((res) => onSave(res.data))
.catch(({ message }) => onError(message));
};
Because it was not a promise, the onSave() function triggered before fetchGeolocation ended. So I have to promisify it. Writing this would work:
function fetchGeolocation () {
return new Promise((resolve, reject) =>{
navigator.geolocation.getCurrentPosition(resolve, reject);
});
};
fetchGeolocation()
.then(res => onSave(res)
.catch(err => onError(err.message);
But I would need to handle all the error codes in the catch callback. I want to handle everything inside the fetchGeolocation function. How to do it?
Thanks!

If I followed your idea properly, then the next snippet might help you out:
const fetchGeoLocation: SearchService["fetchGeoLocation"] = async () => {
return new Promise((resolve, reject) => {
const { geolocation } = navigator;
if (!geolocation) reject("error.geolocation-unavailable");
const handleError = ({ code }) => {
if (code === 1) reject("error.geolocation-permission_denied");
if (code === 2) reject("error.geolocation-unavailable");
if (code === 3) reject("error.geolocation-timeout");
};
const handleSuccess = (position) => {
resolve({ location: [position.coords.longitude, position.coords.latitude] });
};
geo.getCurrentPosition(handleSuccess, handleError, { maximumAge: 10000 });
});
};
Notice instead of throw'ing, it's reject'ing the promise with the error string.

Related

workbox Cache processing does not work with PrecacheController in v6

I was using workbox5.14 (#nuxtjs/pwa) to precache the 'message' event by passing the URL to PrecacheController when received. (from workbox-window)
At this time, PrecacheController.addToCacheList() would not execute the cache, and for some reason PrecacheController.install() would do so.
However, with v6, PrecacheController.install() now requires an 'install' event or an 'activate' event, and I cannot execute PrecacheController.install(event) with a 'message' event. This results in an error.
"sw.js:238 Uncaught (in promise) DOMException: Failed to execute 'waitUntil' on 'ExtendableEvent': The event handler is already finished and no extend lifetime promises are outstanding."
How can we execute PrecacheController's caching process on the 'message' event?
Library Affected:
workbox-precaching
Browser & Platform:
Google Chrome 106.0.5249.103(Official Build)
Issue or Feature Request Description:
v5 ok
/* global importScripts, workbox, consola, processFuncPromise */
const cacheName = workbox.core.cacheNames.precache
const precacheController = new workbox.precaching.PrecacheController(cacheName)
addEventListener('message', async (event) => {
if (event.data.type === 'ADD_PRECACHE') {
const cacheTargetFiles = event.data.payload
const addCaches = async (cacheTargetFiles) => {
for (const file of cacheTargetFiles) {
const cacheKey = precacheController.getCacheKeyForURL(file.filePath)
const cached = await caches
.match(cacheKey)
.then((response) => response !== undefined)
if (!cached) {
precacheController.addToCacheList([{ url: file.filePath, revision: file.revision }])
}
}
}
const checkCaches = async () => {
const cachedList = cacheTargetFiles.map(async (file) => {
const cacheKey = precacheController.getCacheKeyForURL(file.filePath)
const cached = await caches
.match(cacheKey)
.then((response) => response !== undefined)
return cached
})
const cachedListResult = await Promise.all(cachedList)
const cachedListResultFilterd = cachedListResult.filter((response) => {
return response
})
if (cachedListResultFilterd.length === cacheTargetFiles.length) {
return Promise.resolve({ isCompleted: true })
} else {
return Promise.resolve(null)
}
}
await addCaches(cacheTargetFiles)
// ★Caching is started with this INSTALL
precacheController.install()
await processFuncPromise(checkCaches)
self.clients.matchAll().then((clients) =>
clients.forEach((client) => {
client.postMessage({ type: 'FINISHED_ADD_PRECACHE' })
})
)
}
})
addEventListener('install', (event) => {
event.waitUntil(precacheController.install())
event.waitUntil(self.skipWaiting())
})
addEventListener('activate', (event) => {
workbox.precaching.cleanupOutdatedCaches()
event.waitUntil(precacheController.activate())
event.waitUntil(self.clients.claim())
})
addEventListener('fetch', (event) => {
const cacheKey = precacheController.getCacheKeyForURL(event.request.url)
event.respondWith(
caches.match(cacheKey).then(function (response) {
// Cache hit - return the response from the cached version
if (response) {
return response
}
// Not in cache - return the result from the live server
// `fetch` is essentially a "fallback"
return fetch(event.request)
})
)
})
// ↓processFuncPromise()
// export const processFuncPromise = (func, interval = 500) => {
// const retryFunc = (resolve, reject) =>
// func()
// .then((result) => ({ result, isCompleted: result !== null }))
// .then(({ result, isCompleted }) => {
// if (isCompleted) {
// return resolve(result)
// } else {
// return setTimeout(() => retryFunc(resolve, reject), interval)
// }
// })
// .catch(reject)
// return new Promise(retryFunc)
// }
v6 ng
/* global importScripts, workbox, consola, processFuncPromise */
const cacheName = workbox.core.cacheNames.precache
const precacheController = new workbox.precaching.PrecacheController(cacheName)
addEventListener('message', async (event) => {
if (event.data.type === 'ADD_PRECACHE') {
const cacheTargetFiles = event.data.payload
const addCaches = async (cacheTargetFiles) => {
for (const file of cacheTargetFiles) {
const cacheKey = precacheController.getCacheKeyForURL(file.filePath)
const cached = await caches
.match(cacheKey)
.then((response) => response !== undefined)
if (!cached) {
precacheController.addToCacheList([{ url: file.filePath, revision: file.revision }])
}
}
}
const checkCaches = async () => {
const cachedList = cacheTargetFiles.map(async (file) => {
const cacheKey = precacheController.getCacheKeyForURL(file.filePath)
const cached = await caches
.match(cacheKey)
.then((response) => response !== undefined)
return cached
})
const cachedListResult = await Promise.all(cachedList)
const cachedListResultFilterd = cachedListResult.filter((response) => {
return response
})
if (cachedListResultFilterd.length === cacheTargetFiles.length) {
return Promise.resolve({ isCompleted: true })
} else {
return Promise.resolve(null)
}
}
await addCaches(cacheTargetFiles)
// ★ERROR
precacheController.install(event)
await processFuncPromise(checkCaches)
self.clients.matchAll().then((clients) =>
clients.forEach((client) => {
client.postMessage({ type: 'FINISHED_ADD_PRECACHE' })
})
)
}
})
addEventListener('install', (event) => {
precacheController.install(event)
event.waitUntil(self.skipWaiting())
})
addEventListener('activate', (event) => {
workbox.precaching.cleanupOutdatedCaches()
precacheController.activate(event)
event.waitUntil(self.clients.claim())
})
addEventListener('fetch', (event) => {
const cacheKey = precacheController.getCacheKeyForURL(event.request.url)
event.respondWith(
caches.match(cacheKey).then(function (response) {
// Cache hit - return the response from the cached version
if (response) {
return response
}
// Not in cache - return the result from the live server
// `fetch` is essentially a "fallback"
return fetch(event.request)
})
)
})
// ↓processFuncPromise()
// export const processFuncPromise = (func, interval = 500) => {
// const retryFunc = (resolve, reject) =>
// func()
// .then((result) => ({ result, isCompleted: result !== null }))
// .then(({ result, isCompleted }) => {
// if (isCompleted) {
// return resolve(result)
// } else {
// return setTimeout(() => retryFunc(resolve, reject), interval)
// }
// })
// .catch(reject)
// return new Promise(retryFunc)
// }
I asked the question on githubbut could not get an answer, so I came here.

JavaScript, Promise rejection

I'm trying to create this promise:
const getTocStatus = new Promise((resolve, reject) => {
const userInfo = Auth.currentUserInfo();
resolve(userInfo.attributes['custom:tocStatus']);
reject(new Error('Couldn\'t connect to Cognito'));
});
Then use it like this:
getTocStatus.then((response) => {
if (response === 'pending) { //do sth }
}, error => console.log('Error:', error)
But I'm getting the Error:
[TypeError: undefined is not an object (evaluating 'userInfo.attributes['custom:tocStatus']')]
What is badly coded on the promise and it call?
Lionel's answer is correct (I didn't know what Auth.currentUserInfo was, but there's no need for the Promise constructor since you're already dealing with promises:
const getTocStatus = async () => {
try {
const userInfo = await Auth.currentUserInfo()
return userInfo.attributes['custom:tocStatus']
} catch (e) {
new Error("Couldn't connect to Cognito")
}
}
// or with .then syntax
const getTocStatus = () =>
Auth.currentUserInfo()
.then((userInfo) => userInfo.attributes['custom:tocStatus'])
.catch((e) => { Promise.reject(new Error("Couldn't connect to Cognito")) })
The problem is that Auth.currentUserInfo gives you a promise, not a value, so you need to wait for it to complete before you can return its contents. Mario Vernari is also correct in that your error handling has problems too, but that's not why your code is crashing. This should hopefully fix both problems.
const getTocStatus = new Promise(async (resolve, reject) => {
try {
const userInfo = await Auth.currentUserInfo();
resolve(userInfo.attributes['custom:tocStatus']);
} catch (e) {
reject(new Error('Couldn\'t connect to Cognito'));
}
});
You must discriminate when there's an error and when it's not:
const getTocStatus = new Promise((resolve, reject) => {
try {
const userInfo = Auth.currentUserInfo();
resolve(userInfo.attributes['custom:tocStatus']);
}
catch (err) {
reject(new Error('Couldn\'t connect to Cognito'));
}
});
...or something like that.
Finally I did this, but I will fix my code using this:
const getTocStatus = new Promise((resolve, reject) => {
try {
Auth.currentUserInfo()
.then(response => {
resolve(response.attributes['custom:tocStatus'] || TocStatus.CONFIRMED);
})
.catch(err => console.log(err));
} catch (err) {
reject(new Error('Couldn\'t connect to Cognito'));
}
});
And:
getTocStatus.then((response) => {
console.log('response dentro del error', response);
if (response === 'pending') {
// do sth
}
}, error => console.log(error)

cancel multiple promises inside a promise on unmount?

hi i want to cancel promise on unmount since i received warning,
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
My code:
const makeCancelable = (promise: Promise<void>) => {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then(
(val) => (hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)),
(error) => (hasCanceled_ ? reject({ isCanceled: true }) : reject(error))
);
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
},
};
};
useEffect(() => {
const initialize = async () => {
const getImageFilesystemKey = (remoteUri: string) => {
const [_, fileName] = remoteUri.split('toolbox-talks/');
return `${cacheDirectory}${fileName}`;
};
const filesystemUri = getImageFilesystemKey(uri);
try {
// Use the cached image if it exists
const metadata = await getInfoAsync(filesystemUri);
if (metadata.exists) {
console.log('resolve 1');
setFileUri(filesystemUri);
} else {
const imageObject = await downloadAsync(uri, filesystemUri);
console.log('resolve 2');
setFileUri(imageObject.uri);
}
// otherwise download to cache
} catch (err) {
console.log('error 3');
setFileUri(uri);
}
};
const cancelable = makeCancelable(initialize());
cancelable.promise
.then(() => {
console.log('reslved');
})
.catch((e) => {
console.log('e ', e);
});
return () => {
cancelable.cancel();
};
}, []);
but i still get warning on fast press, help me please?
You're cancelling the promise, but you are not cancelling the axios call or any of the logic that happens after it inside initialize(). So while it is true that the console won't print resolved, setFileUri will be called regardless, which causes your problem.
A solution could look like this (untested):
const makeCancelable = (promise: Promise<void>) => {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then(
val => (hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)),
error => (hasCanceled_ ? reject({ isCanceled: true }) : reject(error))
);
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
}
};
};
const initialize = async () => {
const getImageFilesystemKey = (remoteUri: string) => {
const [_, fileName] = remoteUri.split("toolbox-talks/");
return `${cacheDirectory}${fileName}`;
};
const filesystemUri = getImageFilesystemKey(uri);
try {
// Use the cached image if it exists
const metadata = await getInfoAsync(filesystemUri);
if (metadata.exists) {
console.log("resolve 1");
return filesystemUri;
} else {
const imageObject = await downloadAsync(uri, filesystemUri);
console.log("resolve 2");
return imageObject.uri;
}
// otherwise download to cache
} catch (err) {
console.error("error 3", err);
return uri;
}
};
useEffect(() => {
const cancelable = makeCancelable(initialize());
cancelable.promise.then(
fileURI => {
console.log("resolved");
setFileUri(fileURI);
},
() => {
// Your logic is such that it's only possible to get here if the promise is cancelled
console.log("cancelled");
}
);
return () => {
cancelable.cancel();
};
}, []);
This ensures that you will only call setFileUri if the promise is not cancelled (I did not check the logic of makeCancelable).

Dispatch async action

By changing my action to async I am not able to dispatch it. Whenever I make the dispatch it enters the cath with the value false. Here is my page where I dispatch the action from mounted hook (I tried created too)
mounted () {
this.$store.dispatch('productById', this.$route.params['id']).then((response) => {
this.product = response
})
.catch(err => {
console.log(err)
})
}
And this is my action
async productById ({commit}, payload) {
const AuthStr = await getAdminOrRespondentAuth()
return new Promise((resolve, reject) => {
commit(PRODUCT_BY_ID)
axios.get(`${API_BASE}/products/${payload}`, {
params: {
origin: '1'
},
transformRequest: [function (data, headers) {
delete headers.common.Authorization
headers.Authorization = AuthStr
return data
}],
paramsSerializer: params => parseParams(params)
}).then(response => {
if (response.status === 200) {
commit(PRODUCT_BY_ID_SUCCESS, response.data)
resolve(response.data)
} else {
reject(response)
}
})
.catch(err => {
if (err.response.data.idStatus === 1) {
commit(PRODUCT_BY_ID_SUCCESS, err.response.data.data)
reject(err)
}
})
})
}
When the Vue enters mounted hook it "dispatch the action" and goes straight into the catch block not calling my action. My action is not executed.
If I change my action to sync, everything works normally. I need this action to be async because getAdminOrRespondentAuth function thats call oidc async method to recover user.
What I'm doing wrong?
#Samurai8 is right. Thanks for the help. My getAdminOrRespondentAuth was not returning a promise correctly. After correcting the error of the function everything came back to work. This is the function that generate error discribed:
async function getAdminOrRespondentAuth () {
let mgr = new Mgr()
var adminToken = await mgr.getToken()
if (adminToken !== false) {
return 'Bearer '.concat(adminToken)
} else {
let usrToken = localStorage.getItem('user-token')
return 'Bearer '.concat(usrToken)
}
}
Here is the function that works:
async function getAdminOrRespondentAuth () {
var adminToken = ''
return new Promise(async (resolve, reject) => {
let mgr = new Mgr()
try {
adminToken = await mgr.getToken()
} catch (error) {
adminToken = error
}
if (adminToken !== false) {
resolve('Bearer '.concat(adminToken))
} else {
let usrToken = localStorage.getItem('user-token')
if (usrToken !== null) {
resolve('Bearer '.concat(usrToken))
} else {
resolve('')
}
}
})
}

Mock stream finish on jest

I have a function that downloads a file using got and uses fs.createWritableStream to write file on disk. The code is working smooth but the unit test are being a pain:
download(twilioPath, destFile) {
return new Promise(function(resolve, reject) {
const uri = `${TWILIO_BASE_URL}${twilioPath}`.replace('json', 'mp3')
let file = fs.createWriteStream(`/tmp/${destFile}`)
console.log(got)
let str = got.stream(uri)
console.log(uri)
str.on('error', function(err) {
console.log('Error dl', err)
reject(err)
})
str.pipe(file)
file.on('finish', function() {
console.log('banana')
let cb = function() {
console.log('Download completed')
resolve(file)
}
file.close(cb)
})
})
My resolve or reject is not being called and I don't know how to fix that:
const Recording = require('./Recording')
const Readable = require('stream')
var mockedStream = new Readable();
var mockedWrite = new Readable.Writable()
jest.mock('fs', () => ({
createWriteStream: jest.fn((file_name) => {
return mockedWrite;
})
}));
jest.mock('got', () => {
return {
stream: () => {
return mockedStream
}
}
})
describe('Recording', () => {
test('should download a file from twilio', async () => {
...
mockedStream.emit('error')
mockedWrite.emit('finish')
console.log(result)
....
})
})
I've tried to force emit events but no lucky.

Categories

Resources