How to convert the below function which has nested promise and await to just using await or only using promises ?
const test = (url, id) => {
return new Promise((_resolve, _reject) => {
if (isValidUrl(url)) {
let storage = new Storage(Indexdb, id);
const cae = new valueExtract(url);
cae.fetch()
.then(data => {
new zip(data)
.then(obj => obj.getZip())
.then(obj => obj.getList())
.then(list => {
return new Promise(async (resolve, reject) => {
try {
let sI = storage.connection;
await Promise.all(Object.keys(list).map(async (fileName, index) => {
let blob = await new FileExtractor(list[fileName]);
await sI.setItemForce(
fileName,
new StoreObject(
fileName,
'testData',
blob
).dataObject
)
}))
_resolve(sI);
} catch (err) {
_reject(err)
}
})
})
.catch(err => _reject(err))
})
.catch(err => _reject(err))
} else {
_reject('Invalid URL')
}
})
};
I was unable to do the same this what i tried but it never resolves
const test = async (url, id) => {
if (isValidUrl(url)) {
try {
let storage = new Storage(Indexdb, id);
const cae = new valueExtract(url);
const data = await cae.fetch();
return new ZIPExtractor(data)
.then(obj => obj.getZip())
.then(obj => obj.getList())
.then(list => {
return async (resolve, reject) => {
try {
let sI = storage.connection;
await Promise.all(Object.keys(list).map(async (fileName, index) => {
let blob = await new FileExtractor(list[fileName]);
await sI.setItemForce(
fileName,
new StoreObject(
fileName,
'testData',
blob
).dataObject
)
}))
} catch (err) {
throw new Error(err)
}
}
})
.catch(err => _reject(err))
} catch (e) {
throw new Error('Invalid URL')
}
}
};
Also how do we write test case for these kind of a function so that we need not pass in actual network url and mock in jest.
It should fulfill, but with the async (resolve, reject) { … } that you return. You never should've used this in the first place, you can just omit it:
const test = async (url, id) => {
if (!isValidUrl(url)) {
throw new Error('Invalid URL')
}
const storage = new Storage(Indexdb, id);
const cae = new valueExtract(url);
const data = await cae.fetch();
const obj = await new ZIPExtractor(data); // shudder. A constructor should never return a promise
const zip = await obj.getZip();
const list = await zip.getList();
const sI = storage.connection;
await Promise.all(Object.keys(list).map(async (fileName, index) => {
const blob = await new FileExtractor(list[fileName]);
const store = new StoreObject(fileName, 'testData', blob);
await sI.setItemForce(fileName, store.dataObject);
}));
return sI; // or something?
}
Related
I have a function that calls https.get inside a promise which I want to test with Jest.
The function is like this:
const request = (url) => {
return new Promise((resolve, reject) => {
const chunks = [];
https.get(url, (stream) => {
stream
.on('data', (chunk) => {
if( chunk ) {
chunks.push(JSON.parse(chunk));
}
})
.on('error', (err) => {
reject(err);
})
.on('end', () => {
const data = doSomething(chunks);
resolve(data)
});
});
})
}
I want to test that when the function resolves on "end" and rejects on "error";
Currently I have a test like this but because .on("end") doesn't get called, the promise never resolves.
describe("request", () => {
it("Should resolve", async (done) => {
const response = await request("my-url");
expect(response).toEqual("some-data")
})
})
How can I mock events like .on("end") to be called and ensure the promise resolves?
You can do something like this.
// ./request.test.js
jest.mock('https', () => ({
methodToMock: {}
}));
const Stream = require('stream');
const request = require("./request");
const httpsMock = require("https");
describe("request", () => {
it("Should resolve", async () => {
var streamStream = new Stream()
httpsMock.get = jest.fn().mockImplementation((url, cb) => {
cb(streamStream)
streamStream.emit('data', 'some');
streamStream.emit('data', '-');
streamStream.emit('data', 'data');
streamStream.emit('end'); // this will trigger the promise resolve
})
const response = await request("my-url");
expect(response).toEqual("some-data");
})
})
const https = require("https");
const request = (url) => {
return new Promise((resolve, reject) => {
const chunks = [];
https.get(url, (stream) => {
stream
.on('data', (chunk) => {
if (chunk) {
// chunks.push(JSON.parse(chunk));
chunks.push(chunk);
}
})
.on('error', (err) => {
reject(err);
})
.on('end', () => {
// const data = doSomething(chunks);
const data = chunks.join('');
resolve(data)
});
});
})
}
module.exports = request;
Note that jest.mock('https', ...) need to be called before const request = require("./request"); if you want https to be mocked.
I'm always getting response as 0 for this count but is 1 when I query DB straight. What am I missing here?
const getItemsCount = (db, applicationId, typeId) => new Promise((resolve, reject) => {
const existsStatement = `SELECT COUNT(ITEMIDENTIFIER) AS ITCOUNT
FROM LISTITEMS
WHERE WORKLISTREFERENCEIDENTIFIER = '${applicationId}'
AND TYPEID = '${typeId}'`;
db.executeQuery(existsStatement)
.then((result) => {
resolve(parseInt(result[0].ITCOUNT, 10));
})
.catch((error) => {
reject(error);
});
});
populate(applicationId, data) {
const ITM = this;
ITM.populatePromise = new Promise(async (resolve, reject) => {
if (!data || !data.typeId) {
reject(new Error('Body parameters missing'));
}
await queries.getWorklistItemsCount(ITM.db, applicationId, data.typeId).then((result)=>{
resolve(result)
})
})
}
I want to ask, how do you run ascyronus promises in the promise all? when I run the function that I made Promise.all runs all the functions in parallel (syncronus)
if you run the code below then the result is
// jon
// andrey
// tania
// JON
// ANDREY
// TANIA
here is my code
// First promise returns an array
const getUsers = () => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve([{ id: 'jon' }, { id: 'andrey' }, { id: 'tania' }]), 600)
})
}
// Second promise relies on the resulting array of first promise
const getIdFromUser = users => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(users.id), 500)
})
}
// Third promise relies on the result of the second promise
const capitalizeIds = id => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(id.toUpperCase()), 200)
})
}
const newPromise = async user => {
const userId = await getIdFromUser(user)
console.log(userId) //jon
const capitalizedId = await capitalizeIds(userId)
console.log(capitalizedId) //JON
return
}
const runAsyncFunctions = async () => {
const users = await getUsers()
await Promise.all(users.map(user => newPromise(user)))
}
runAsyncFunctions()
but I want the results as below, this will work if the promise all is run asyncronus
// jon
// JON
// andrey
// ANDREY
// tania
// TANIA
and this is my playground code playground
If you want to initialize a Promise only after the last Promise has finished - that is, run them in serial, not in parallel - then .map and Promise.all is not the right tool for the job, because that'll initialize all Promises at once. await in a for loop instead:
const runAsyncFunctions = async() => {
const users = await getUsers()
for (const user of users) {
await newPromise(user);
}
}
// First promise returns an array
const getUsers = () => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve([{
id: 'jon'
}, {
id: 'andrey'
}, {
id: 'tania'
}]), 600)
})
}
// Second promise relies on the resulting array of first promise
const getIdFromUser = users => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(users.id), 500)
})
}
// Third promise relies on the result of the second promise
const capitalizeIds = id => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(id.toUpperCase()), 200)
})
}
const newPromise = async user => {
const userId = await getIdFromUser(user)
console.log(userId) //jon
const capitalizedId = await capitalizeIds(userId)
console.log(capitalizedId) //JON
return
}
const runAsyncFunctions = async() => {
const users = await getUsers()
for (const user of users) {
await newPromise(user);
}
}
runAsyncFunctions()
If your real code allows you to do the logging outside, you can keep running the Promises in parallel, but only log the results in order once they're all completed:
const newPromise = async user => {
const userId = await getIdFromUser(user)
const capitalizedId = await capitalizeIds(userId)
return [userId, capitalizedId];
}
const runAsyncFunctions = async() => {
const users = await getUsers()
const names = await Promise.all(users.map(user => newPromise(user)));
for (const name of names.flat()) {
console.log(name);
}
}
// First promise returns an array
const getUsers = () => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve([{
id: 'jon'
}, {
id: 'andrey'
}, {
id: 'tania'
}]), 600)
})
}
// Second promise relies on the resulting array of first promise
const getIdFromUser = users => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(users.id), 500)
})
}
// Third promise relies on the result of the second promise
const capitalizeIds = id => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(id.toUpperCase()), 200)
})
}
const newPromise = async user => {
const userId = await getIdFromUser(user)
const capitalizedId = await capitalizeIds(userId)
return [userId, capitalizedId];
}
const runAsyncFunctions = async() => {
const users = await getUsers()
const names = await Promise.all(users.map(user => newPromise(user)));
for (const name of names.flat()) {
console.log(name);
}
}
runAsyncFunctions()
Promise all runs like a funny wrapper around a normal array interator function...
let results = Promise.all(myarray.map(item => {
try {
return await functionThatReturnsAPromise()
} catch (error) {
console.log(error)
return null
}
}))
A good alternative to Promise all is for of because you can put await on it...
for await (let item of myarray) {
// Try catch etc
}
Node will be fine with this, but you might want to check browser support if you're running it in the front-end.
the getIdFromUser is executed async. This means on the next line, the whole array is already there and that is why it is printed in a serial manner. Fetching both the userId and the capitalized firstly will result in what you want
// First promise returns an array
const getUsers = () => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve([{ id: 'jon' }, { id: 'andrey' }, { id: 'tania' }]), 600)
})
}
// Second promise relies on the resulting array of first promise
const getIdFromUser = async users => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(users.id), 0)
})
}
// Third promise relies on the result of the second promise
const capitalizeIds = id => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(id.toUpperCase()), 0)
})
}
const newPromise = async user => {
const userId = await getIdFromUser(user)
const capitalizedId = await capitalizeIds(userId)
console.log(userId) //jon
console.log(capitalizedId) //JON
return
}
const runAsyncFunctions = async () => {
const users = await getUsers()
console.log(users)
await Promise.all(users.map(user => newPromise(user)))
}
runAsyncFunctions()
I have tried to make it simple and removed functions that are not required, I can prodcue the requested output in question
You can alos check here solution
// First promise returns an array
const getUsers = () => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve([{ id: 'jon' }, { id: 'andrey' }, { id: 'tania' }]), 600)
})
}
// Second promise relies on the resulting array of first promise
const getIdFromUser = users => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(users.id), 500)
})
}
// You do not need this, why make it complicated ??
// Third promise relies on the result of the second promise
// const capitalizeIds = async id => {
// return new Promise((resolve, reject) => {
// return setTimeout(() => resolve(id.toUpperCase()), 200)
// })
// }
function capitalizeIds(id)
{
return id.toUpperCase();
}
const runAsyncFunctions = async () => {
const users = await getUsers()
users.map(user => {
console.log(user.id) //jon
const capitalizedId = capitalizeIds(user.id);
console.log(capitalizedId) //JON
})
}
runAsyncFunctions()
const test = async (url, id) => {
if (!isValidUrl(url)) {
throw new Error('Invalid URL')
}
const storage = new Storage(Indexdb, id);
const cae = new valueExtract(url);
const data = await cae.fetch();
const obj = await new ZIPExtractor(data); // shudder. A constructor should never return a promise
const zip = await obj.getZip();
const list = await zip.getList();
const sI = storage.connection;
await Promise.all(Object.keys(list).map(async (fileName, index) => {
const blob = await new FileExtractor(list[fileName]);
const store = new StoreObject(fileName, 'testData', blob);
await sI.setItemForce(fileName, store.dataObject);
}));
return sI; // or something?
}
This is the answer of a question i had asked , I wanted to also know how do we test such nested awaited functions.
unit Test of Normal Fun
test('Tes scenario', () => {
let storage = new StorageAdapter(
type,
dbName
)
expect(storage.storageName).toBe(dbName)
expect(storage.storageType).toEqual(type)
expect(storage.connection).not.toBeNull()
})
I'd like to know how I could resolve and go the next array value only after the first stream has ended, my current code just executes streams for all urls instead of doing it one by one
const downloadUrl = (url) => {
console.log(url);
console.log("Started :" + url.name);
const writer = fs.createWriteStream(url.name);
return new Promise(resolve => {
return axios({
url: url.url,
responseType: 'stream',
})
.then(response => {
const totalLength = response.headers['content-length'];
const stream = response.data;
let chunksDone = 0;
stream.on('data', (chunk) => {
const chunkLength = chunk.length;
chunksDone += chunkLength;
const percent = ((chunksDone / totalLength) * 100).toFixed(2);
printProgress(url.name, percent)
writer.write(new Buffer(chunk));
})
return streamToPromise(stream);
})
.then(() => {
debugger;
writer.end();
})
.catch(log);
})
}
const streamToPromise = (stream) => {
return new Promise(function (resolve, reject) {
stream.on("end", resolve);
stream.on("error", reject);
})
}
const urls = ['some.mp3','someother.mp3'];
const promise = urls.reduce(function (acc, item) {
return downloadUrl(item)
}, Promise.resolve);
return new Promise(resolve => {
return axios({
url: url.url,
responseType: 'stream',
})
…
}
This returns a promise that never resolves. Drop the new Promise wrapper and return axios(… directly.
const promise = urls.reduce(function (acc, item) {
return downloadUrl(item)
}, Promise.resolve);
This doesn’t wait for any promises. It looks like you were going for:
const promise = urls.reduce(function (acc, item) {
return acc.then(() => downloadUrl(item));
}, Promise.resolve());
Also, there shouldn’t be a need to copy chunk with new Buffer before writing it.