const getInfoFromURL = async() => {
const a = await test1();
const b = await test2();
};
const test1 = async() => {
setTimeout(() => {
console.log('test 1');
},6000);
};
const test2 = async() => {
setTimeout(() => {
console.log('test 2');
},6000);
};
getInfoFromURL();
Example case: after 6 sec shown message /test1/ , and then after another 6 sec shown message /test2/.
Result: after 6 sec immediately show 2 message test1 and test2.
Can i realize this case withot using promise?
I'm assuming what you actually want to achieve is running const a = await test1(); and const b = await test2(); in parallell. With that in mind I've taken the liberty to change the surrounding mockup code a bit to show you how to do it in parallell:
const getInfoFromURL = async() => {
const asyncResult1 = test1(), asyncResult2 = test2();
return [await asyncResult1, await asyncResult2];
};
const test1 = async() => {
return await new Promise(resolve => setTimeout(resolve, 6000)).then(() => 'test1');
};
const test2 = async() => {
return await new Promise(resolve => setTimeout(resolve, 6000)).then(() => 'test2');
};
console.time('timer');
getInfoFromURL().then((result) => { console.timeEnd("timer"); console.log(result)});
This should output timer: 600*.***ms and ['test1', 'test2'].
I've changed the test functions to use promises and .then return "test1" and "test2", and for the actual call of getInfoFromUrl() to use .then to allow us to console log when it resolves (so we can actually measure the time it takes) as I consider these changes not relevant to the problem you're trying to solve.
Anyway the important part is the content in the getInfoFromURL function. Note that writing return [await test1(), await test2()] will NOT work and they will be run sequentially.
The key is assigning the result of the functions to variables first (without awaiting the result), and then awaiting the same variables at a later time. In this case, the async operations have both already started before we're awaiting them.
Related
I have a set of callbacks that may run on different durations before I close my web app. I also have a timeout where if it reaches past the timeout duration, I also close the application. The reason for this is to prevent the callbacks from blocking in closing the web app if it passes timeout duration.
Here is my current solution:
public close() {
const callbacks = this.onBeforeCloseCallbacks.map((cb) => new Promise(res => res(cb())));
const timeout = new Promise((res) => setTimeout(res, TIMEOUT_DURATION));
await Promise.race([Promise.all(callbacks), timeout]).then((value) => {
// Currently returns Promise.all(callbacks) right away
console.log(value)
});
await this.pluginEngine.close();
}
}
These are my tests
it('Should still close the plugin when timing out', async () => {
// Arrange
const cleanupMock = jest.fn();
const cb1 = jest.fn().mockReturnValue(async () => new Promise(resolve => setTimeout(() => resolve(console.log('cb1')), 3000)));
const cleanupMock2 = jest.fn();
const cb2 = jest.fn().mockReturnValue(async () => new Promise(resolve => setTimeout(() => resolve(console.log('cb2')), 11000)));
const placementCloseService = new PlacementCloseService(integrationMock, pluginInterface);
// Act
// onBeforeClose is registering callbacks that needs to be run before close
placementCloseService.onBeforeClose(cb1);
placementCloseService.onBeforeClose(cb2);
await placementCloseService.close();
// Assert
expect(cleanupMock).toBeCalled();
expect(cleanupMock2).not.toBeCalled();
expect(pluginInterface.context.close).toBeCalled();
});
My current solution is returning Promise.all(callbacks) even if it hasn't called expected callbacks to run yet. What I expect to happen is that it passes through my timeout instead since it has a timer of 4000 and the last closeCallback has a timer of 5000.
What am I doing wrong?
Your closeCallbacks are not async, You need them to return a promise.
const closeCallbacks = [
// for sample purposes. i assigned timeouts to mock that it takes longer to run these callbacks then my timeout duration
async () => new Promise(resolve => setTimeout(() => resolve(console.log('cb1')), 3000)),
async () => new Promise(resolve => setTimeout(() => resolve(console.log('cb2')), 5000)),
];
This creates a parameter named async not an async function
(async) => {}
Your timeout function never calls res parameter
const wait = (cb, time) => new Promise(r => { cb(); r() }, time)
const closeCallbacks = [
() => wait(() => console.log('cb1'), 3000),
() => wait(() => console.log('cb2'), 5000)
];
const timeout = new Promise((res) => setTimeout(() => console.log('timeout'); res(), 4000));
To make my code cleaner I want to use fetched API data in a few different functions, instead of one big. Even though I 've did manage to reffer to that data in other functions, the problem is the API im a fetching throws different, randomized results every time it is called. And so the output from userData() does not equal that from userData2(), even though my intention is different and I'd like the result variable contents to be the same between functions.
const getData = () =>
fetch("https://opentdb.com/api.php?amount=10").then((response) =>
response.json()
);
const useData = async () => {
const result = await getData();
console.log(result);
};
const useData2 = async () => {
const result = await getData();
console.log(result);
};
Your getData() function returns a promise. One fun fact about promises is that while they can only resolve once, that resolved value can be accessed and used as many times as you want.
const dataPromise = getData();
const useData = async () => {
const result = await dataPromise;
console.log(result);
};
const useData2 = async () => {
const result = await dataPromise;
console.log(result);
};
Using await resolves the promise value, the equivalent of...
dataPromise.then((result) => {
console.log(result);
});
// or `dataPromise.then(console.log)` if you like brevity
I like to point this out about the fetch-api... you should always check the Response.ok property
const getData = async () => {
const res = await fetch("https://opentdb.com/api.php?amount=10");
if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`);
}
return res.json();
};
If I have these lines:
const service = await getService('player');
const players = await service.players();
Can I use this one-liner version instead? Are they equivalent?
const players = await (await getService('player')).players();
Is there an even more streamlined way to write it?
Short answer: It's equivalent to in terms of result.
However, As #CertainPerformance's comment, you should use the first snippet to be able to debugger (as well as to follow best practice) like this.
let getService = (str) => new Promise(resolve => setTimeout(() => resolve({players: getPlayers}), 1000));
let getPlayers = () => new Promise(resolve => setTimeout(() => resolve("Response data"), 1000));
async function run() {
const service = await getService('player');
console.log(service); // debugger to watch response of each async func
const players = await service.players();
console.log(players); // debugger to watch response of each async func
}
run();
// The Below code already contains the suggestions from the answers and hence works :)
within the below script I tried to fully execute the 'createDatabase' function before the .then call at the end starts to run. Unfortunately I couldn't figure out a solution to achieve just that.
In generell the flow should be as followed:
GetFiles - Fully Execute it
CreateDatabase - Fully Execute it (while awaiting each .map call to finish before starting the next)
Exit the script within the .then call
Thanks a lot for any advise :)
const db = require("../database")
const fsp = require("fs").promises
const root = "./database/migrations/"
const getFiles = async () => {
let fileNames = await fsp.readdir(root)
return fileNames.map(fileName => Number(fileName.split(".")[0]))
}
const createDatabase = async fileNumbers => {
fileNumbers.sort((a, b) => a - b)
for (let fileNumber of fileNumbers) {
const length = fileNumber.toString().length
const x = require(`.${root}${fileNumber.toString()}.js`)
await x.create()
}
}
const run = async () => {
let fileNumbers = await getFiles()
await createDatabase(fileNumbers)
}
run()
.then(() => {
console.log("Database setup successfully!")
db.end()
process.exitCode = 0
})
.catch(err => {
console.log("Error creating Database!", err)
})
The x.create code looks as follows:
const dbQ = (query, message) => {
return new Promise((resolve, reject) => {
db.query(query, (err, result) => {
if (err) {
console.log(`Error: ${err.sqlMessage}`)
return reject()
}
console.log(`Success: ${message}!`)
return resolve()
})
})
}
x.create = async () => {
const query = `
CREATE TABLE IF NOT EXISTS Country (
Code CHAR(2) UNIQUE NOT NULL,
Flag VARCHAR(1024),
Name_de VARCHAR(64) NOT NULL,
Name_en VARCHAR(64) NOT NULL,
Primary Key (Code)
)`
const result = await dbQ(query, "Created Table COUNTRY")
return result
}
If you want each x.create to fully execute before the next one starts, i.e. this is what I interpret where you say while awaiting each .map call to finish before starting the next - then you could use async/await with a for loop as follows:
const createDatabase = async fileNumbers => {
fileNumbers.sort((a, b) => a - b);
for (let fileNumber of fileNumbers) {
const x = require(`.${root}${fileNumber.toString()}.js`);
await x.create();
})
}
However, this also assumes that x.create() returns a Promise - as you've not shown what is the typical content of .${root}${fileNumber.toString()}.js file is, then I'm only speculating
The other interpretation of your question would simply require you to change createDatabase so that promises is actually an array of promises (at the moment, it's an array of undefined
const createDatabase = async fileNumbers => {
fileNumbers.sort((a, b) => a - b);
const promises = fileNumbers.map(fileNumber => {
const x = require(`.${root}${fileNumber.toString()}.js`);
return x.create();
})
await Promise.all(promises);
}
Now all .create() run in "parallel", but createDatabase only resolves onces all promises returned by x.create() resolve - again, assumes that x.create() returns a promise
Why do these two code snippets output differently? In the first one, when I console out, I get pending Promises (when I want the actual value).
(() => {
let message = {
actions: [{}]
};
message.actions = message.actions.map(async action => {
action.result = {};
action.result.startAt = await "whatever";
return action;
});
console.log(message);
})();
And in this second code snippet, I get the actual resolved value...
( async () => {
let message = {
actions: [{}]
};
message.actions[0].result = {};
message.actions[0].result.startAt = await "whatever";
console.log(message);
})();
First case:
You started async task and without waiting for it completion requesting value of the task (not sure why).
Second case:
You fill the structure and print it immediately so it works.
Yet await "whatever"; is just "whatever"
If the procedure is asynchronous you can use Promise.all() or a for loop to await each element of message.actions to return a Promise value
(async () => {
let message = {
actions: [{}]
};
message.actions = await Promise.all(message.actions.map(async(action) => {
action.result = {};
action.result.startAt = await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 1000), "whatever"));
return action;
}));
console.log(message);
})();