I am experimenting with async/await, I can't understand why this line :
resolvedValue = await this.tryToSolve()
gives me this error :
Unexpected token this
class Test {
constructor() {
this.method = 0
this.checkLink()
}
async checkLink() {
return new Promise((resolve, reject) => {
let resolvedValue
for (let i = 0; i < 3; i++) {
this.method = i
resolvedValue = await this.tryToSolve()
if (resolvedValue) break
}
console.log(`Method ${this.method} did the trick.`);
resolve(resolvedValue)
})
}
tryToSolve() {
return new Promise((resolve, reject) => { // Resolves if this.method==1
console.log(`Trying to solve with method ${this.method}...`);
setTimeout(() => {
resolve(!!this.method ? `http://www${this.method}.someurl.com` : false)
}, 1000)
})
}
}
const test = new Test()
Does anyone know the correct syntax to store the result of an async method in a variable?
Thanks in advance.
To keep things simple, it happens because when you create a Promise, in its' constructor you pass an arrow function, which contains await call. You must always put async keyword before the declaration of a function, that contains await.
So, instead of doing this
async checkLink() {
return new Promise((resolve, reject) => {
let resolvedValue
for (let i = 0; i < 3; i++) {
this.method = i
resolvedValue = await this.tryToSolve()
if (resolvedValue) break
}
console.log(`Method ${this.method} did the trick.`);
resolve(resolvedValue)
})
}
Do it like this
checkLink() {
return new Promise(async (resolve, reject) => {
let resolvedValue
for (let i = 0; i < 3; i++) {
this.method = i
resolvedValue = await this.tryToSolve()
if (resolvedValue) break
}
console.log(`Method ${this.method} did the trick.`);
resolve(resolvedValue)
})
}
More info: https://ponyfoo.com/articles/understanding-javascript-async-await#using-async-await
Drop the new Promise around the await! You want only
async checkLink() {
let resolvedValue
for (let i = 0; i < 3; i++) {
this.method = i
resolvedValue = await this.tryToSolve()
if (resolvedValue) break
}
console.log(`Method ${this.method} did the trick.`);
return resolvedValue;
}
or much simpler
async checkLink() {
for (let i = 0; i < 3; i++) {
const value = await this.tryToSolve()
if (value) {
console.log(`Method ${i} did the trick.`);
return value;
}
}
}
Related
I am ordering some items with their priorities. I used a loop for that. However, I get some weird outputs like [1,1,2,2,3] instead of [1,2,3,4,5](these are priorities btw). The loop is below.
const switchPriority = async function(catId, srcI, destI){
let indexofCat;
try {
for (let i = 0; i < data[0].length; i++) {
const element = data[0][i];
if(element.id === catId){
indexofCat = i;
}
}
let Item = Parse.Object.extend('Item')
let itemQuery = new Parse.Query(Item)
for (let i = (srcI>destI?destI:srcI); i < (srcI>destI?(srcI+1):(destI+1)); i++) {
let id = data[1][indexofCat][i].id;
let item = await itemQuery.get(id);
item.set("priority",i+1);
await item.save();
}
} catch (error) {
alert(error)
}
}
Why there is such a problem? When I add alert to the loop, with some delay it gives proper outputs. How can I solve this ? I am appreciate for your help.
I don't know if you forgot, but it's necessary use the word async like this await works. You can insert your code into a function:
async function parse(){
for (let i = (srcI); i < (destI); i++) {
// alert("index in loop "+ i);
let id = data[1][indexofCat][i].id;
let item = await itemQuery.get(id);
// alert(item.get("name"));
item.set("priority",i+1);
await item.save();
}
}
Look at for more details: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/async_function
You can also promisify a function that you needs to guarantee that it be executed before the next instruction. How?
function createPromise (parameter) {
return new Promise((resolve, reject) => {
resolve(console.log(parameter))
})
}
async function test(){
console.log('First')
await createPromise('Promise')
console.log('Second')
}
test()
Look at: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise
I need a loop to play 10 sounds in sequence. My first attempt had the sounds overlapping, so I was told that I need to use Promise/await. The code below plays sound 0 then never continues the loop.
(The library I'm using (jscw) is for morse code. You pass it a string, it plays the morse equivalent. Its "onFinished" calls a user-defined function.)
async function playAll() {
for (let i = 0; i < 10; i++) {
playMorse(words[i]);
await playstate();
}
}
function playstate() {
playdone = true;
//console.log(playdone);
return new Promise((resolve) => {
window.addEventListener('playdone', resolve)
})
}
function playMorse(z) {
var m = new jscw();
playdone = false;
m.onFinished = function() {
playstate();
}
m.play(z);
}
It seems nothing is supposed to fire the playdone event you are listening for.
So a simple solution is to fire it in the onFinished callback.
const words = ["hello", "world"];
async function playAll() {
for (let i = 0; i < 2; i++) {
console.log("###READING", words[i]);
playMorse(words[i]);
await playstate();
}
}
function playstate() {
playdone = true;
//console.log(playdone);
return new Promise((resolve) => {
window.addEventListener('playdone', resolve, { once: true })
})
}
function playMorse(z) {
var m = new jscw();
playdone = false;
m.onFinished = function() {
dispatchEvent( new Event("playdone") );
}
m.play(z);
}
btn.onclick = playAll;
<script src="https://fkurz.net/ham/jscwlib/src/jscwlib.js"></script>
<button id="btn">play all</button>
But you don't need an event here, simply make playMorse return a Promise that will resolve in the onFinished callback:
const words = ["hello", "world"];
async function playAll() {
for (let i = 0; i < 2; i++) {
console.log("###READING", words[i]);
await playMorse(words[i]);
}
}
function playMorse(z) {
return new Promise( (resolve) => {
const m = new jscw();
m.onFinished = resolve;
m.play(z);
});
}
btn.onclick = playAll;
<script src="https://fkurz.net/ham/jscwlib/src/jscwlib.js"></script>
<button id="btn">play all</button>
can someone try to fix my problem?
I am trying to get this output:
100x do C
done B
done A
But everytime i get this one:
10x do C
done B
done A
Is it possible to get it without using async/await feature?
function a() {
let myPromise = new Promise(function(myResolve, myReject) {
b();
myResolve()
});
myPromise.then(function() {
console.log('done A')
});
}
function b() {
let myPromise = new Promise(function(myResolve, myReject) {
for (i = 0; i < 10; i++) {
c();
}
myResolve()
});
myPromise.then(function() {
console.log('done B')
});
}
function c() {
for (i = 0; i < 10; i++) {
console.log('do C')
}
}
a()
The problem is that you're not declaring i, so you're creating an implicit global variable that both b and c write to. (I wrote an article about these years ago: The Horror of Implicit Globals.)
In the below, I'm going to make it three runs of three instead of ten runs of ten, just so we can see it more clearly (and so I don't have to increase the size of the in-snippet console's history — it has an option for that, but I don't recall the details).
I strongly recommend using strict mode so that this is the error it always should have been. Here's your code in strict mode and with rejection handlers added:
"use strict";
function a() {
let myPromise = new Promise(function(myResolve, myReject) {
b();
myResolve()
});
myPromise.then(function() {
console.log('done A')
}).catch(function(err) {
console.error(err);
});
}
function b() {
let myPromise = new Promise(function(myResolve, myReject) {
for (i = 0; i < 3; i++) {
c();
}
myResolve()
});
myPromise.then(function() {
console.log('done B')
}).catch(function(err) {
console.error(err);
});
}
function c() {
for (i = 0; i < 3; i++) {
console.log('do C')
}
}
a()
If you declare i (in both places), you'll get the result you expect.
function a() {
let myPromise = new Promise(function(myResolve, myReject) {
b();
myResolve()
});
myPromise.then(function() {
console.log('done A')
});
}
function b() {
let myPromise = new Promise(function(myResolve, myReject) {
for (let i = 0; i < 3; i++) { // ***
c();
}
myResolve()
});
myPromise.then(function() {
console.log('done B')
});
}
function c() {
for (let i = 0; i < 3; i++) { // ***
console.log('do C')
}
}
a()
but, beware that the reason you're getting the output you're getting is that everything except the calls to the fulfillment handlers is synchronous. If instead of a for loop you were doing some asynchronous process, there's nothing preventing you from getting done B or done A before all of the work done by c is done, because there's nothing connecting those promises together.
Let's see that. Suppose we have an asynchronous version of console.log that waits 10ms before writing the log entry (but we'll use console.log to write out done A and done B immediately, so you can see when they ran):
function asyncLog(...msgs) {
return new Promise(resolve => {
setTimeout(() => {
console.log(...msgs);
resolve();
}, 10);
});
}
Let's look at what happens:
function asyncLog(...msgs) {
return new Promise(resolve => {
setTimeout(() => {
console.log(...msgs);
resolve();
}, 10);
});
}
function a() {
let myPromise = new Promise(function(myResolve, myReject) {
b();
myResolve();
});
myPromise.then(function() {
console.log('done A');
});
}
function b() {
let myPromise = new Promise(function(myResolve, myReject) {
for (let i = 0; i < 3; i++) {
c();
}
myResolve()
});
myPromise.then(function() {
console.log('done B')
});
}
function c() {
for (let i = 0; i < 3; i++) {
asyncLog('do C');
}
}
a();
Notice how things aren't connected to each other.
To connect them, you'd have to make b wait for c's promise to be fulfilled, and make a wait for b's promise to be fulfilled. Something like this:
function asyncLog(...msgs) {
return new Promise(resolve => {
setTimeout(() => {
console.log(...msgs);
resolve();
}, 10);
});
}
function a() {
let myPromise = new Promise((resolve, reject) => {
resolve(b());
});
return myPromise
.then(() => {
console.log('done A');
});
}
function b() {
let myPromise = Promise.resolve();
for (let i = 0; i < 3; i++) {
myPromise = myPromise.then(c);
}
return myPromise
.then(() => {
console.log('done B')
});
}
function c() {
let p = Promise.resolve();
for (let i = 0; i < 3; i++) {
p = p.then(() => asyncLog('do C'));
}
return p;
}
a()
.then(() => {
console.log("all done");
})
.catch(error => {
console.error(`Error: ${error.message}`);
});
I have an async function that has a loop that I need to be able to pause or unpause it. This is what I have so far.
I use a flag to pause the flow:
let flag = true;
function flag_func() {
flag = !flag;
}
$(document).ready(function () {
function sleep(ms) {
while (!flag) {
//...waiting.. but infinite loop
}
return new Promise(resolve => setTimeout(resolve, ms));
}
async function show_simulation(data) {
document.getElementById("solve-button").outerHTML = "<button type=\"button\" id='pause-button' onclick='flag_func()' class=\"btn btn-primary btn-lg\">Pause</button>";
//simulation
if (data.length === 0) {
console.log('stuff')
} else {
let i;
for (i = 0; i < data.length; i++) {
await sleep(40);
// do stuff
}
}
}
});
The problem is that is being paused, but due the while block the flow, I can't unpause the for loop.
Any idea about how I can solve this?
It might be a nice use case for async iterables. It involves a bit of boilerplate to create your async list, but then the code is much nicer. Basically you would have:
import AsyncList from './async-list.js'
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
async function f(data) {
const list = new AsyncList(data);
document.getElementById("btn-toggle").addEventListener("click", function () {
if (list.paused) {
this.textContent = "Pause";
list.resume();
} else {
this.textContent = "Resume";
list.pause()
}
})
for await (let item of list) {
console.log(item)
await sleep(1000);
}
console.log("end of loop")
}
f([10, "hello", 1029, 90, 80, 209, 44])
A possible implementation of AsyncList could be:
export default class AsyncList {
constructor(array) {
// shallow copy
this._array = array.slice();
this._index = 0;
this._length = this._array.length;
this.paused = false;
this._resume = () => {}; // noop, in case `resume` is called before `pause`
}
[Symbol.asyncIterator]() {
return this;
}
pause() {
this.paused = true;
}
resume() {
this.paused = false;
this._resume();
}
next() {
if (this._index < this._length) {
const value = this._array[this._index++];
if (this.paused) {
return new Promise(r => this._resume = r.bind(null, { value }))
}
return Promise.resolve({ value })
} else {
return Promise.resolve({ done: true });
}
}
}
Just to give to you the idea, you could also encapsulate the private properties, and check more scenarios (here I assume data is an array, for example, not just an iterable).
I'd replace:
let i;
for (i = 0; i < data.length; i++) {
await sleep(40);
// do stuff
}
...with...
let i = 0;
const doStuff = () => {
// do stuff
if (++i < data.length) {
setTimeout(doStuff, 40);
}
};
setTimeout(doStuff, 40);
I was practicing Express 4.x and noticed the following:
app.get('/fake', function(req, res) {
var obj = [];
for (let i = 0; i < 3; i++) {
jsf.resolve(fakeSchema).then(function(iter) {
obj.push(iter);
});
}
res.send(obj);
});
So, going to that route, I get "[ ]", while I was expecting to receive an array of 3 (fake) documents.
FYI, when logging each loop, I can clearly see the documents generated, even inside the array.
Any explanation?
Your jsf.resolve functiion is async so you can use async/await for this to perform task in sync manner.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
app.get('/fake', async function(req, res) {
var obj = [];
for (let i = 0; i < 3; i++) {
try {
var iter = await jsf.resolve(fakeSchema);
obj.push(iter);
} catch (e) {}
}
res.send(obj);
});
Although #Nishant's provided answer works, I suggest using this approach.
let jsf = {};
// faking your jsf.resolve method
jsf.resolve = (param) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Math.random());
}, 1000);
})
};
let fakeSchema = {};
let obj = [];
let promises = [];
for (let i = 0; i !== 3; i++) {
promises.push(jsf.resolve(fakeSchema).then(function (iter) {
obj.push(iter);
}));
}
Promise.all(promises).then(() => {
console.log(obj);
});
This allows all the promises to run concurrently, imagine your jsx.resolve takes a long time to complete, using await would freeze your entire appp.
As opposed to this. Note the runtime.
(async () => {
let jsf = {};
// faking your jsf.resolve method
jsf.resolve = (param) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Math.random());
}, 1000);
})
};
let fakeSchema = {};
let obj = [];
for (let i = 0; i !== 3; i++) {
obj.push(await jsf.resolve(fakeSchema));
}
console.log(obj);
})();
#Nishant Dixit's answer also correct!
You can try this simple solution also, if you like :
app.get('/fake', function(req, res) {
var obj = [];
for (let i = 0; i < 3; i++) {
try {
jsf.resolve(fakeSchema).then(function(iter) {
obj.push(iter);
res.send(obj);
} catch (e) {
res.send(e);
}
});
};
});