I'm trying to get Number 1 to console.log first but it's not. What am I doing wrong?
Thank you.
let timer = function(time, message) {
setTimeout((time) => {
console.log(`ALERT: ${message}`);
}, time);
//return time;
}
const asyncDemo = async function asyncDemo(time, message) {
try {
var time1 = await timer(3000, "Number 1");
} catch (e) {
console.log("Critical error!");
}
try {
var time2 = await timer(1000, "Number 2");
} catch (e) {
console.log("Critical error!");
}
}
asyncDemo();
Ansync/Await depends on promises to work. So your function needs to return a promise. For instance:
let timer = function(time, message) {
return new Promise((resolve, reject) =>{
setTimeout((time) => {
console.log(`ALERT: ${message}`);
resolve()
}, time);
})
}
Related
I would like to have a cancelable promise that reports progress at the same time. Something like a combination of p-progress and p-cancelable. And while usage of any of them separately is simple I'm struggling a bit to combine them both.
This is what I tried so far which successfully reports progress but throws cancelablePromise.cancel is not a function error.
import PCancelable from 'p-cancelable';
import PProgress from 'p-progress';
const cancelablePromise = doJobWithProgress();
try {
cancelablePromise.onProgress((progress) => {
console.log('progress: ' + progress);
});
await sleep(500);
cancelablePromise.cancel();
const result = await cancelablePromise;
console.log("result", result);
}
catch (error) {
console.log("Main catch error: " + error);
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function doJobWithProgress() {
return PProgress((progressCallback) => {
return new PCancelable((resolve, reject, onCancel) => {
try {
let progress = 0;
const interval = setInterval(() => {
progress += 0.1;
progressCallback(progress);
}, 100);
const timeout = setTimeout(() => {
const result = 1;
clearTimeout(timeout);
clearInterval(interval);
resolve(result);
}, 1000);
onCancel(() => {
console.log('canceled');
clearTimeout(timeout);
clearInterval(interval);
});
}
catch (error) {
console.log('Promise catch error: ' + error);
reject(error);
}
});
});
}
To make this complete - I ended up, as #Bergi suggested, with a different approach i.e., using AbortController and extending the EventEmitter class (I'm coding in NodeJS - for the web you can inherit EventTarget).
Maybe someone will be interested in answer to the original question - when trying to combine the two libraries I ended up creating a new CancelableProgressPromise class and copy-pasting the most necessary parts from the p-progress library to it and extending the PCancelable:
import PCancelable from "p-cancelable";
export default class CancelableProgressPromise extends PCancelable {
constructor(executor) {
const setProgress = progress => {
if (progress > 1 || progress < 0) {
throw new TypeError('The progress percentage should be a number between 0 and 1');
}
(async () => {
// We wait for the next microtask tick so `super` is called before we use `this`
await Promise.resolve();
// Note: we don't really have guarantees over
// the order in which async operations are evaluated,
// so if we get an out-of-order progress, we'll just discard it.
if (progress <= this._progress) {
return;
}
this._progress = progress;
for (const listener of this._listeners) {
listener(progress);
}
})();
};
super((resolve, reject, onCancel) => {
executor(
value => {
setProgress(1);
resolve(value);
},
reject,
onCancel,
progress => {
if (progress !== 1) {
setProgress(progress);
}
},
);
});
this._listeners = new Set();
this._setProgress = setProgress;
this._progress = 0;
}
get progress() {
return this._progress;
}
onProgress(callback) {
if (typeof callback !== 'function') {
throw new TypeError(`Expected a \`Function\`, got \`${typeof callback}\``);
}
this._listeners.add(callback);
return this;
}
}
Usage example:
import CancelableProgressPromise from './cancelableProgressPromise.js';
const progressablePromise = doJobWithProgress();
try {
progressablePromise.onProgress((progress) => {
console.log('progress: ' + progress);
});
await sleep(500);
progressablePromise.cancel();
const result = await progressablePromise;
console.log("result", result);
}
catch (error) {
console.log("Main catch error: " + error);
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function doJobWithProgress() {
return new CancelableProgressPromise((resolve, reject, onCancel, progressCallback) => {
try {
let progress = 0;
const interval = setInterval(() => {
progress += 0.1;
progressCallback(progress);
}, 100);
const timeout = setTimeout(() => {
const result = 1;
clearTimeout(timeout);
clearInterval(interval);
resolve(result);
}, 1000);
onCancel(() => {
console.log('canceled');
clearTimeout(timeout);
clearInterval(interval);
});
} catch (error) {
reject(error);
}
});
}
// Output:
// progress: 0.1
// progress: 0.2
// progress: 0.30000000000000004
// progress: 0.4
// canceled
// Main catch error: CancelError: Promise was canceled
I am trying to write a Javascript promise that resolves and rejects the desired variables. I am getting the error message below after running in the console
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().
code: 'ERR_UNHANDLED_REJECTION'
Here is my code
Js File:
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
let grimePromise = new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject("User Left");
} else if (userWatchingGrimeVids) {
reject("This user is a G");
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
grimePromise.then((message) => {
console.log("Success: " + message);
});
grimePromise.catch((message) => {
console.log("You Failed: " + message);
});
By calling the .then() and the .catch() handler on two different places, you actually end up with two different promises - each of .then() and .catch() returns a new one. The promise returned by .then() will not be caught by the separate .catch() handler:
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
let grimePromise = new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject("User Left");
} else if (userWatchingGrimeVids) {
reject("This user is a G");
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
grimePromise
.then((message) => {
console.log("Success: " + message);
})
.catch(() => console.log(1));
grimePromise.catch((message) => {
console.log(2);
console.log("You Failed: " + message);
});
Therefore, one promise gets turned into two. Each of them will adopt the state of the original promise. When the original is rejected, both the derived promises reject. Yet only one is handled.
Instead, you can directly specify the .catch() in the same promise chain as the .then():
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
let grimePromise = new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject("User Left");
} else if (userWatchingGrimeVids) {
reject("This user is a G");
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
grimePromise
.then((message) => {
console.log("Success: " + message);
})
.catch((message) => {
console.log("You Failed: " + message);
});
<h1>Check the browser console - no unhandled promise rejections</h1>
Or alternatively, specify both handlers in the then():
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
let grimePromise = new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject("User Left");
} else if (userWatchingGrimeVids) {
reject("This user is a G");
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
grimePromise
.then(
(message) => {
console.log("Success: " + message);
},
(message) => {
console.log("You Failed: " + message);
}
);
<h1>Check the browser console - no unhandled promise rejections</h1>
The reason this isn't working is because you didn't have a .catch() on the first one. It was throwing a rejection, but you weren't handling it gracefully with a catch, so it threw.
Here is your code working - I just moved your catch block to go after the .then:
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
const grimePromise = new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject('User Left');
} else if (userWatchingGrimeVids) {
reject('This user is a G');
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
grimePromise
.then((message) => {
console.log('Success: ' + message);
})
.catch((message) => {
console.log('You Failed: ' + message);
});
Just as a tip though, you should only reject whenever the function is unsuccessful in some way. That could be a timeout, a network error, incorrect input etc. Whatever is inside of a reject should ideally be an error.
Additionally, I'd recommend using async await, as it's much easier to read and more manageable. You can easily do this by creating a function which returns your new Promise, then awaiting it like so:
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
const grime = () => {
return new Promise((resolve, reject) => {
if (grimeUserLeft) {
reject(new Error('User Left'));
} else if (userWatchingGrimeVids) {
resolve('This user is a G');
} else {
resolve("Congrats. Your channel doesn't suck");
}
});
};
const doStuff = async () => {
try {
const result = await grime();
console.log(result);
} catch (error) {
console.error(error);
}
};
doStuff();
To make this truly asynchronous, you should ideally be doing some async stuff inside of the promise. This can be demonstrated with a setTimeout:
const grimeUserLeft = false;
const userWatchingGrimeVids = true;
const grime = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (grimeUserLeft) {
reject(new Error('User Left'));
} else if (userWatchingGrimeVids) {
resolve('This user is a G');
} else {
resolve("Congrats. Your channel doesn't suck");
}
}, 2000);
});
};
const doStuff = async () => {
try {
const result = await grime();
console.log('result finished after 2 seconds');
console.log(result);
} catch (error) {
console.error(error);
}
};
doStuff();
I have this:
this.toggleWaiting()
this.results = await this.query(term)
this.toggleWaiting()
First a loading spinner gets triggered.
Then the query runs.
And when the query function ended the loading spinner gets closed.
But what if I want to just show the loading spinner, when the query takes maybe more then 0.5 seconds?
Is there a simple way to do this?
A way to achieve this is to pass the this.query(term) promise to a function which will handle triggering the toggleWaiting only when the query takes longer than the specified amount of time (using the timeout).
For example, the below takes a promise, a function (waitingFn) which will be called with the isWaiting status as well as a timeout which you can use to specify how long you want to wait before you show the loading spinner. Finally, when the promise has been fulfilled, we return the result:
async function handleWaiting(promise, waitingFn, timeout) {
let loadingStarted = false;
let timeoutInstance = null;
const timeoutPromise = new Promise((res) => {
timeoutInstance = setTimeout(() => {
loadingStarted = true;
waitingFn(true);
}, timeout);
return res();
});
function onFinished() {
clearTimeout(timeoutInstance);
if (loadingStarted) {
waitingFn(false);
}
}
try {
const [result] = await Promise.all([promise, timeoutPromise]);
onFinished();
return result;
} catch (ex) {
onFinished();
throw ex;
}
}
You can call the handleWaiting function like so:
const result = await handleWaiting(this.query(term), (isWaiting) => this.toggleWaiting(), 500);
As #FZs and #Bergi have pointed out (thank you both), the below is an antipattern due to using the promise constructor:
function handleWaiting(promise, waitingFn, timeout) {
return new Promise((res, rej) => {
let loadingStarted = false;
const timeoutInstance = setTimeout(() => {
loadingStarted = true;
waitingFn(true);
}, timeout);
function onFinished() {
if (loadingStarted) {
waitingFn(false);
}
clearTimeout(timeoutInstance);
}
return promise
.then((result) => {
onFinished();
res(result);
})
.catch((ex) => {
onFinished();
rej(ex);
});
});
}
Thanks to ljbc1994 I found a nice solution.
Inside my alpineJs object - I have this implementation:
{
waiting: false,
async handleWaiting(promise, timeout) {
return new Promise((res, rej) => {
let loadingStarted = false;
const timeoutInstance = setTimeout(() => {
loadingStarted = true;
this.waiting = true;
}, timeout);
const onFinished = () => {
if (loadingStarted) {
this.waiting = false;
}
clearTimeout(timeoutInstance);
}
promise
.then((result) => {
onFinished();
res(result);
})
.catch((ex) => {
onFinished();
rej(ex);
});
});
},
async searchForTerm(term) {
this.results = await this.handleWaiting(this.$wire.query(term), 500);
// do something with the results...
},
}
Pretty straightforward.
For someone who is interested in the full code - here is the commit inside the github repo:
https://github.com/MichaelBrauner/sunfire-form/commit/7c1f8270e107a97b03264f5ddc5c3c3ae6f7cfd7
Let's say that the button is clicked one after another.
In this case, I do not want the functionC() to run at every click.
However, every click functionA() and functionB() must work.
I want the function C() to work according to the dataA and dataB values in the last click.
For example, if the button is clicked 100 times and the button is not clicked again within 5 seconds after the last click, the functionC() should work according to the latest dataA and dataB values.
async function functionABC() {
try {
var dataA = await functionA();
} catch (error) { console.log(error); }
try {
var dataB = await functionB();
} catch (error) { console.log(error); }
try {
await functionC(dataA, dataB);
} catch (error) { console.log(error); }
}
<Button title="Buton" onPress={() => functionABC()}/>
NEW DESCRIPTION
(1)
The button was clicked.
functionA and functionB worked.
It was clicked again before the 5 second period expired.
functionC not worked.
(2)
functionA and functionB worked.
It was clicked again before the 5 second period expired.
functionC not worked.
...
(n-1)
functionA and functionB worked.
It was clicked again before the 5 second period expired.
functionC not worked.
(n)
functionA and functionB worked.
5 seconds have expired.
functionC worked.
What you need is to somehow store an identifier for "last running function call" so that you can make your check before functionC. Using a Symbol works well in this case, but any other unique ID would be fine (for example an incremental integer, or an empty object).
let lastClick
async function functionABC() {
const thisClick = Symbol()
lastClick = thisClick
try {
var dataA = await functionA();
} catch (error) { console.log(error); }
try {
var dataB = await functionB();
} catch (error) { console.log(error); }
try {
if(lastClick === thisClick)
await functionC(dataA, dataB);
} catch (error) { console.log(error); }
}
function functionA() {
console.log('run A')
return new Promise(resolve => setTimeout(resolve, 500))
}
function functionB() {
console.log('run B')
return new Promise(resolve => setTimeout(resolve, 500))
}
function functionC() {
console.log('run C')
return new Promise(resolve => setTimeout(resolve, 500))
}
<button onclick="functionABC()"> click me </button>
If you really need to artificially extend the time it takes for functionA and functionB to resolve (and to give your user more time to click again before calling functionC), you could use Promise.all as the simplest solution.
Note that this solution will call functions A & B concurrently. Also, I didn't take care of the error handling. This is just to give you ideas.
let lastClick
async function functionABC() {
const thisClick = Symbol()
lastClick = thisClick
const [dataA, dataB] = await Promise.all([
functionA(),
functionB(),
new Promise(resolve => setTimeout(resolve, 5000))
])
if(thisClick === lastClick)
functionC(dataA, dataB)
}
function functionA() {
console.log('run A')
return new Promise(resolve => setTimeout(resolve, 500))
}
function functionB() {
console.log('run B')
return new Promise(resolve => setTimeout(resolve, 500))
}
function functionC() {
console.log('run C')
return new Promise(resolve => setTimeout(resolve, 500))
}
<button onclick="functionABC()"> click me </button>
I am not sure exactly what you are looking for but according to your example I assumed that this is what you might be looking for.
let counter = 0;
async function functionABC() {
counter++;
try {
var dataA = await functionA();
} catch (error) { console.log(error); }
try {
var dataB = await functionB();
} catch (error) { console.log(error); }
if(counter % 100 === 0) {
setTimeout(() => {
if(counter % 100 === 0) {
try {
await functionC(dataA, dataB);
} catch (error) { console.log(error); }
}
}, 5000)
}
}
<Button title="Buton" onPress={() => functionABC()}/>
Please let me know if not and what exactly you wish to acheive.
EDIT
let timestamp = 0;
let firstClicked = false;
async function functionABC() {
const currentTimestamp = Date.now();
try {
var dataA = await functionA();
} catch (error) { console.log(error); }
try {
var dataB = await functionB();
} catch (error) { console.log(error); }
if(firstClicked) {
if(currentTimestamp - timestamp >= 5000) {
try {
await functionC(dataA, dataB);
} catch (error) { console.log(error); }
}
}
}
firstclicked = true;
timestamp = currentTimestamp;
}
<Button title="Buton" onPress={() => functionABC()}/>
I asked a question two days ago with a reply "Your method must return a Promise (or an Observable)."
I have altered my code to be exactly the same as the example at "https://firebase.google.com/docs/firestore/manage-data/transactions" but the problem is it passes the result as a console log but I need to wait for the write to complete at I need the result.
orderIndex() {
let db = this.firebase.firestore();
var sfDocRef = db.collection("cities").doc("SF");
db.runTransaction(function (transaction) {
return transaction.get(sfDocRef).then(function (sfDoc) {
if (!sfDoc.exists) {
throw "Document does not exist!";
}
var newPopulation = sfDoc.data().population + 1;
if (newPopulation <= 1000000) {
transaction.update(sfDocRef, { population: newPopulation });
return newPopulation;
} else {
return Promise.reject("Sorry! Population is too big.");
}
});
}).then(function (newPopulation) {
console.log("Population increased to ", newPopulation);
}).catch(function (err) {
// This will be an "population is too big" error.
console.error(err);
});
}
I have spent a further two days trying to get a promise returned.
I have seen so many questions asking for help and receiving code suggestions in reply. Please help because I am new to this and have spent over four days on this problem.
By the way the code from firebase.google has an error in
return Promise.reject("Sorry! Population is too big.");
Error: "[ts] Property 'reject' does not exist on type '(resolver: (resolve: (val: IWhenable) => void, reject: (reason: any) => void, notify: (prog...'."
My previous question was at "How do I alter the promises in my function to stop it returning before the data arrives?"
Your function is not returning the promise and also in the then case you are not returning any value.
Try this:
orderIndex() {
let db = this.firebase.firestore();
var sfDocRef = db.collection("cities").doc("SF");
return db.runTransaction(function (transaction) { //Return here
return transaction.get(sfDocRef).then(function (sfDoc) {
if (!sfDoc.exists) {
throw "Document does not exist!";
}
var newPopulation = sfDoc.data().population + 1;
if (newPopulation <= 1000000) {
transaction.update(sfDocRef, { population: newPopulation });
return newPopulation;
} else {
return Promise.reject("Sorry! Population is too big.");
}
});
}).then(function (newPopulation) {
console.log("Population increased to ", newPopulation);
return newPopulation; //Return the value
}).catch(function (err) {
// This will be an "population is too big" error.
console.error(err);
});
}
The following code updates the index, stores it back in firestore and returns the new number.
createOrderNo() {
const getDbIndex = new Promise(
(resolve, reject) => {
if (!this.orderLive) {
this.orderLive = true;
const sfDocRef = this.db.collection('eOrderIndex').doc('orderIndex');
sfDocRef.get().
then(function (sfDoc) {
if (!sfDoc.exists) {
throw "Document does not exist!";
}
console.log('sfDoc.data()', sfDoc.data()['index'])
let index = sfDoc.data()['index'] + 1;
sfDocRef.update({ index: index });
resolve(index);
})
} else {
const reason = new Error('Already live');
reject(reason);
}
})
async function show(index) {
return new Promise(
(resolve, reject) => {
var message = 'New index ' + index;
resolve(message);
}
);
};
// call the promise
async function runPromise() {
try {
console.log('Before get');
let index = await getDbIndex;
let message = await show(index);
console.log(message);
console.log('After get');
}
catch (error) {
console.log(error.message);
}
}
(async () => {
await runPromise();
})();
}
Many thanks to Jecelyn Yeen at scotch.io