I have a typescript module.
public multipleQuery(queries: string[]) {
return new Promise(async (resolve, reject) => {
const cPool = new sql.ConnectionPool(this.room.db);
await cPool.connect().then((pool: any) => {
const transaction = new sql.Transaction(pool);
return transaction.begin(async (err: any) => {
const request = new sql.Request(transaction, { stream: true });
try {
queries.forEach(async (q) => {
await request.query(q);
});
transaction.commit((err2: any) => {
pool.close();
if (err2) {
reject(err2);
} else {
resolve(true);
}
});
} catch (err) {
transaction.rollback(() => {
pool.close();
reject(err);
});
}
});
}).catch((err: Error) => {
cPool.close();
reject(err);
});
});
}
queries variable is an array of string, I put inside a lot of sql insert queries.
No matter what I write in queries, I still receive this error, why?
RequestError: Requests can only be made in the LoggedIn state, not the
SentClientRequest state TransactionError: Can't acquire connection for
the request. There is another request in progress.
the solutions is to use async
const async = require("async");
public multipleQuery(queries: string[]) {
return new Promise((resolve, reject) => {
const pool = new sql.ConnectionPool(this.room.db);
return pool.connect().then((p: any) => {
const transaction = new sql.Transaction(p);
return transaction.begin((err: any) => {
const request = new sql.Request(transaction);
if (err) {
reject(err);
}
return async.eachSeries(queries, async (query: any, callback: any) => {
return request.query(query);
}, async (err2: any) => {
if ( err2 ) {
await transaction.rollback(() => {
pool.close();
reject(err2);
});
} else {
await transaction.commit(() => {
pool.close();
resolve(true);
});
}
});
});
});
});
}
Related
I have a task with a promise and I don't understand how to do it.please help
1.Imports function "func1" from file "script1.js";
const func1 = a => {
switch (a) {
case 'a':
return new Promise((resolve) => {
setTimeout(() => {
resolve('result1');
}, 100);
});
case 'b':
return new Promise((resolve) => {
setTimeout(() => {
resolve('result2');
}, 100);
});
default:
return new Promise((resolve) => {
setTimeout(() => {
resolve('result3');
}, 100);
});
};
};
module.exports = func1;
Reads a string from the "input.txt";
input a
Calls "func1" with an argument equal to the string;
Waits until the received Promise has state: "fulfilled" and then outputs the result to the file "output.txt".
this is how i try to solve but nothing works:
const fs = require('fs')
const func1 =require ("./script1 (1)")
fs.readFile('./input.txt', 'utf8' , (err, data) => {
if (err) {
console.error(err)
return
}
console.log(data)
async function one (data) {
try {
const result = await Promise(func1);
console.log(result);
} catch (err) {
console.log(err)
}}
fs.writeFile("output.txt",one().toString(), function(err)
{
if (err)
{
return console.error(err);
}
})
})
the result must be "result1"
To call a Promise you can use async await and try catch blocks. Once you have the asynchronous result value, you can call fs.writeFile(). Try this:
func1.js:
const func1 = a => {
switch (a) {
case 'a': return new Promise((resolve) => {
setTimeout(() => { resolve('result1'); }, 100);
});
case 'b': return new Promise((resolve) => {
setTimeout(() => { resolve('result2'); }, 100);
});
default: return new Promise((resolve) => {
setTimeout(() => { resolve('result3'); }, 100);
});
};
};
module.exports = func1;
index.js:
const fs = require('fs');
const func1 = require("./demo2.js")
fs.readFile('./input.txt', 'utf8' , async (err, data) => {
if (err) {
console.error(err);
return;
}
//console.log(data)
try {
const result = await func1(data);
console.log(result);
fs.writeFile("output.txt", result, function(err) {
if (err){
return console.error(err);
}
});
} catch (err) {
console.log(err)
}
});
the async/await way is to change const result = await Promise(func1); to const result = await func1(data); you can also use then like this const result = func1(data).then(res => res);
and a better func1 would be
const func1 = a => {
return new Promise((resolve) => {
setTimeout(() => {
switch(a) { // handle cases };
});
});
};
module.exports = func1;
const func1 = a => {
return new Promise((resolve) => {
switch (a) {
case 'a':
setTimeout(() => {
resolve('result1');
}, 100);
case 'b':
setTimeout(() => {
resolve('result2');
}, 100);
default:
setTimeout(() => {
resolve('result3');
}, 100);
};
})
};
module.exports = func1;
const fs = require('fs')
const func1 =require ("./script1 (1)")
fs.readFile("./input.txt", "utf8", (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
async function one(data) {
try {
func1(data).then(result => {
console.log(result)
})
} catch (err) {
console.log(err);
}
}
fs.writeFile("output.txt", one().toString(), function (err) {
if (err) {
return console.error(err);
}
});
});
I have a class :
class RequestTimeout {
constructor(timeoutMilliseconds) {
this.timeoutMilliseconds = timeoutMilliseconds;
this.timeoutID = undefined;
}
start() {
return new Promise((resolve, reject) => {
this.timeoutID = setTimeout(() => reject(new Error(`Request attempt exceeded timeout of ${this.timeoutMilliseconds}`)), this.timeoutMilliseconds);
});
}
clear() {
if (this.timeoutID) clearTimeout(this.timeoutID);
}
}
module.exports = RequestTimeout;
This class is used in a module:
const RequestTimeout = require('./request-timeout');
function Request() {
...
async function withTimeout(request, ms) {
const timeout = new RequestTimeout(ms);
return Promise.race([
request(),
timeout.start(),
])
.then(
response => {
timeout.clear();
return response;
},
err => {
timeout.clear();
throw err;
}
);
}
...
}
How do i mock RequestTimeout in a test using Request? For example:
it('should clear the timeout following a successful response', async () => {
nock('http://example.com')
.get('/')
.reply(200, { example: true });
const response = await request.get({ ...baseOptions });
expect(response.example).toEqual(true);
});
// MOCK
let mockGetTimeOutId = jest.fn();
jest.mock('../request-timeout', () => {
return jest.fn().mockImplementation((ms) => {
let timeoutId = undefined;
return {
start: () => new Promise((resolve, reject) => {
timeoutId = setTimeout(() => reject(), ms);
}),
clear: () => mockGetTimeOutId(timeoutId),
}
})
});
// TEST
it('should clear the timeout following a successful response', async () => {
nock('http://example.com')
.get('/')
.reply(200, { example: true });
expect(mockGetTimeOutId).toHaveBeenCalledTimes(0);
const response = await request.get({ ...baseOptions });
expect(mockGetTimeOutId).toHaveBeenCalledTimes(1);
expect(response.example).toEqual(true);
});
The following code takes a good 5 to 10 seconds to resolve (there are 5 API-calls). It's not usable as it is. Is the slow response my fault or is the Coinbase API just slow and large?
app.get('/buys', async (req, res) => {
const buys = await getAllBuys()
res.json(buys)
})
const fetchAllAccounts = () => {
return new Promise(
(resolve, reject) => {
coinbase.getAccounts(null, (err, accounts) => {
if (err) {
reject(err)
}
else{
resolve(accounts)
}
})
}
)
}
const fetchAllBuys = (account) => {
return new Promise(
(resolve, reject) => {
account.getBuys(null, (err, buys) => {
if (err) {
reject(err)
}
else{
resolve(buys)
}
})
}
)
}
const getAllBuys = async () => {
const accounts = await fetchAllAccounts()
let combinedBuys = []
for (let account of accounts) {
const buys = await fetchAllBuys(account)
combinedBuys = [...combinedBuys, ...buys]
}
//console.log(combinedBuys)
return combinedBuys
}
Response is a array with items that include account information like certificates and so on.
Ok, so it was my code. I the code above I wait for each request to finish before firing off a new one. Which results in unnecessary delay. With the Promise.all method we can generate all the promises and resolve them at the same time. Look at the refactored code bellow.
app.get('/buys', async (req, res) => {
console.time('[/buys]');
const promises = await getAllBuys()
const buys = await Promise.all(promises)
res.json(buys)
console.timeEnd('[/buys]');
})
const fetchAllAccounts = () => {
return new Promise(
(resolve, reject) => {
coinbase.getAccounts(null, (err, accounts) => {
if (err) {
reject(err)
}
else{
resolve(accounts)
}
})
}
)
}
const fetchAllBuys = (account) => {
return new Promise(
(resolve, reject) => {
account.getBuys(null, (err, buys) => {
if (err) {
reject(err)
}
else{
resolve(buys)
}
})
}
)
}
const getAllBuys = async () => {
const accounts = await fetchAllAccounts()
let promises = []
for (let account of accounts) {
promises.push(fetchAllBuys(account))
}
return promises
}
From 2,5 / 3 seconds to 0,8 / 1 second.
I am trying to download tracks via the soundcloud API, and then launch a callback once an indeterminant amount of tracks is downloaded. When I run the below code, I see "All done" being console logged before anything else, even though I intend for it to be the last thing... What am I doing wrong?
// Deps
import fs from 'fs'
import SC from 'node-soundcloud'
import request from 'request'
// Write mp3 function
function writeMP3(track) {
return new Promise((resolve, reject) => {
console.log('Starting download: ', track.title)
request.get(track.download_url)
.on('error', err => {
// reject('Download error: ', err)
})
.on('finish', () => {
() => resolve('Download complete')
})
.pipe(fs.createWriteStream(`./data/temp/${track.title}_${track.user.username}.mp3`))
})
}
async function asyncTrackFetch(track) {
return await writeMP3(track)
}
// Array of promises to callback upon
const trackActions = []
SC.init({
id: 'MY_ID',
secret: 'MY_SECRET'
})
SC.get('/tracks', (err, tracks) => {
if (err) {
throw new Error(err)
} else {
console.log('Tracks fetched: ', tracks.length)
tracks.map(track => {
if (track.downloadable) {
console.log('downloadable')
trackActions.push(asyncTrackFetch(track))
}
})
}
})
// Perform requests async
Promise.all(trackActions).then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
Promise.all(trackActions) waits on whatever promises are in trackActions, but trackActions is empty at the time you make the call. You're only adding promises to the array after your SC.get callback gets called.
Try putting your Promise.all... block inside the SC.get callback like this:
SC.get('/tracks', (err, tracks) => {
if (err) {
throw new Error(err)
} else {
console.log('Tracks fetched: ', tracks.length)
tracks.map(track => {
if (track.downloadable) {
console.log('downloadable')
trackActions.push(asyncTrackFetch(track))
}
})
Promise.all(trackActions).then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
}
})
It's worth mentioning as well that your line throw new Error(err) will crash the program since there's nowhere for that error to be caught.
As Antonio Val mentioned, there are better ways to do this. If you promisify the node-soundcloud library then the last part of your code could look like this:
SC.get('/tracks').then(tracks => {
// No need for trackedActions array.
return Promise.all(tracks.filter(track => track.downloadable)
.map(track => asyncTrackFetch(track)))
}).then(fetchedTracks => {
console.log('All done fetching tracks', fetchedTracks)
}).catch(err => {
// Handle error.
})
Or inside an async function,
try {
const tracks = await SC.get('/tracks')
const fetchPromises = tracks
.filter(track => track.downloadable)
.map(track => asyncTrackFetch(track))
const fetchedTracks = await Promise.all(fetchPromises)
console('All done fetching tracks.', fetchedTracks)
} catch (err) {
// Handle error
}
I think the easiest way would be to move Promise.all after tracks.map loop finished.
A more elegant solution would be to promisify SC.get as well and use async await along all your code.
UPDATE:
Couldn't test it so not sure if it works, but it would be something like this:
import fs from 'fs'
import SC from 'node-soundcloud'
import request from 'request'
function writeMP3(track) {
return new Promise((resolve, reject) => {
console.log('Starting download: ', track.title)
request.get(track.download_url)
.on('error', err => {
// reject('Download error: ', err)
})
.on('finish', () => {
() => resolve('Download complete')
})
.pipe(fs.createWriteStream(`./data/temp/${track.title}_${track.user.username}.mp3`))
})
}
function getTracks() {
return new Promise((resolve, reject) => {
SC.get('/tracks', (err, tracks) => {
if (err) {
return reject(err)
}
console.log('Tracks fetched: ', tracks.length)
resolve(tracks)
})
})
}
SC.init({
id: 'MY_ID',
secret: 'MY_SECRET'
})
With async await:
async function start() {
const tracks = await getTracks();
for (let track of tracks) {
await writeMP3(track)
}
}
start()
.then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
.catch((err) => {
// insert error handler here
})
If you just want to use Promises:
getTracks
.then((tracks) => {
const promiseArray = tracks.map((track) => {
return writeMP3(track)
})
return Promise.all(promiseArray)
})
.then(() => {
console.log('All done')
console.log(fs.readdirSync('./data/temp'))
})
.catch((err) => {
// insert error handler here
})
As you can see in the .gif below, I call tape.js twice in my integration tests and it works fine. But the second time I run the tests, after I've commented out the code to insert test data into my database, the body of the tape.js function never runs.
Why does this happen?
I've put a breakpoint in my database code and it finishes and calls resolve() before the next tape.js test runs so I don't get it.
Here's my test class, followed by database class:
import tape = require('tape');
const testData = require('../../../helper/data');
import * as db from '../../../helper/integration/database';
helper.setTestEnvironmentVariableOn();
const startedServer : hapi.Server = require('../../../../src/app');
(async function runTests() {
recreateDatabase();
await db.runQuery(getInsertGetRolesTestPermissionsSql());
await test_getRoles_returnsAllRoles();
recreateDatabase();
await db.runQuery(getInsertGetUsersRolesTestPermissionsSql());
await test_getUsersRoles_userCanGetHerOwnRoles();
await startedServer.stop();
})();
function test_getRoles_returnsAllRoles() {
return new Promise( (resolve, reject) => {
tape(testeeName + 'GET /roles returns all, and only, Ekaya roles', async (assert) => {
await helper.createNewUser();
superagent
.get(testData.url + `roles`)
.set('token', testData.token)
.end( (error: any , result: superagent.Response) => {
assert.equals(error, null);
assert.end();
resolve();
});
});
});
}
function test_getUsersRoles_userCanGetHerOwnRoles() {
return new Promise( (resolve, reject) => {
tape(testeeName + 'GET /users/id/roles gives a user her own roles', async (assert) => {
const userid = '635de6dc-0df9-43f4-96dc-922bca541515';
const token = await helper.getTokenForUser(userid);
superagent
.get(testData.url + `users/`+userid+`/roles`)
.set('token', token)
.end( (error: any , result: superagent.Response) => {
assert.equals(error, null);
assert.end();
resolve();
});
});
});
}
function getInsertGetUsersRolesTestPermissionsSql() : string {
return `
INSERT INTO enum.permissions(permissions_id, name,...
`;
}
function getInsertGetRolesTestPermissionsSql() {
return `
delete fr...`;
}
Database class:
import * as pg from "pg";
import 'reflect-metadata';
const Config = require('../../../src/config');
const config = new Config();
export function runQuery(sql: string) : Promise<any> {
return new Promise<any>(async (resolve, reject) => {
try {
const _db = new pg.Client(config.ekaya_local_connection_string);
await connect(_db);
_db.query(sql, function(error, result) {
if (error) {
console.error('Error running query', error);
_db.end();
reject(error);
return;
}
_db.end();
resolve(result);
});
}
catch (error) {
reject(error);
}
});
}
function connect(_db: pg.Client) : Promise<void> {
return new Promise<void>( function(resolve, reject) {
try {
_db.connect(function(error) {
if (error) {
console.error('Could not connect to postgres in integration test helper', error);
reject(error);
return;
}
resolve(null);
});
}
catch (error) {
reject(error);
}
});
}
UPDATE - after vitaly-t's suggestion
New database code, no effect on the problem:
import 'reflect-metadata';
const Config = require('../../../src/config');
const config = new Config();
const pgp = require("pg-promise")();
export function runQuery(sql: string) : any {
return new Promise<any>(async (resolve, reject) => {
try {
const _db = pgp(config.ekaya_local_connection_string);
const result = await _db.query(sql);
pgp.end();
resolve({rows: result});
}
catch (error) {reject(error);}
});
}