Async await not working for function - create pdf document and then email it - async-await

const pdf = require('pdfkit')
const QR = require('qrcode')
const emailTickets = async (userEvent, tickets) => {
await createQRCodes(tickets)
await createTicketPDF(tickets, userEvent)
await sendGridEmail(emailDetails, true)
}
The problem seems to be that async/await isn't working properly for the createTicketPDF function.
When I run the above emailTickets function a blank pdf document is emailed. However, when I run it with the below setTimeOut, the pdf contains all the detail that I want.
const emailTickets = async (userEvent, tickets) => {
await createQRCodes(tickets)
await createTicketPDF(tickets, userEvent)
setTimeout(async() => {await sendGridEmail(emailDetails, true)}, 5000);
}
I would prefer a solution where the code waited for the createTicketPDF function to finish before calling the sendGridEmail function.
Below is the code for the createTicketPDF function:
const createTicketPDF = async (tickets, userEvent) => {
const doc = new pdf
doc.pipe(fs.createWriteStream(`./tickets/${tickets[0]._id}.pdf`))
await tickets.map(async(ticket, index)=> {
return(
await doc.fontSize(30),
await doc.text(userEvent.title),
await doc.fontSize(10),
await doc.moveDown(),
await doc.text(`Venue: ${userEvent.venue}, ${userEvent.address1} `),
await doc.moveDown(),
await doc.fontSize(20),
await doc.text(`Ticket ${index+1} of ${tickets.length}: ${ticket.ticketType}`),
await doc.image(`./qrCodes/${ticket._id}.png`, {
align: 'center',
valign: 'center'
}),
await doc.addPage()
)
})
doc.end()
}
I had a forEach loop in this function but replaced it with a map on learning that forEach loops don't work well with async await.
I have also tried using (const ticket of tickets){} but this didn't work either
VS code is suggesting that none of the awaits within the map do anything
For completeness, below is the createQRCodes. This function is working perfectly.
const createQRCodes = async (tickets) => {
let CreateQRCodes = tickets.map(ticket => {
return(
QR.toFile(`./qrCodes/${ticket._id}.png`, String([ticket._id, ticket.randomNumber, ticket.creationTime.getTime(), ticket.userEvent]))
)})
await Promise.all(CreateQRCodes)
}
Any ideas where I am going wrong

Twilio SendGrid developer evangelist here.
As far as I can tell from the documentation, PDFKit is a synchronous library. The only thing that is asynchronous is writing the PDF to disk. So your code should not need to await anything within the createTicketPDF function. You do need to listen for the stream to finish though, so you could return a promise that resolves when the stream is finished, like this:
const createTicketPDF = (tickets, userEvent) => {
const doc = new pdf
const stream = doc.pipe(fs.createWriteStream(`./tickets/${tickets[0]._id}.pdf`))
tickets.map((ticket, index)=> {
return(
doc.fontSize(30),
doc.text(userEvent.title),
doc.fontSize(10),
doc.moveDown(),
doc.text(`Venue: ${userEvent.venue}, ${userEvent.address1} `),
doc.moveDown(),
doc.fontSize(20),
doc.text(`Ticket ${index+1} of ${tickets.length}: ${ticket.ticketType}`),
doc.image(`./qrCodes/${ticket._id}.png`, {
align: 'center',
valign: 'center'
}),
doc.addPage()
)
})
doc.end()
const promise = new Promise((resolve) => {
stream.on("finish", () => {
resolve();
})
});
return promise;
}
const emailTickets = async (userEvent, tickets) => {
await createQRCodes(tickets)
await createTicketPDF(tickets, userEvent)
await sendGridEmail(emailDetails, true)
}

Related

How make make await as useDispatch

Can you please explain why await on dispatch has no effect?.Use dispatch is synchronous by default. But is there any way to make it to async?
I have one issue by using dispatch and createAsyncthunk.I think it halts the render of other components. I may be wrong please suggest a better way to handle this rendering issue. I think dispatch is still synchronous.
//API services
const getPersonLists = async (query) => {
return await axios.get(`${endPoint}/person?page=${query.page}&perPage=${query.perPage}`);
};
const fetchPeronWithAsyncThunk = createAsyncThunk('userSlice/userList', async (query) => await getPersonLists(query));
//Slice
const userSlice = createSlice({
name: 'userSlice',
initialState: {
users: [],
loading: false,
},
extraReducers: {
[fetchPeronWithAsyncThunk.pending]: (state) => {
state.loading = true;
},
[fetchPeronWithAsyncThunk.rejected]: (state) => {
state.loading = false;
},
[fetchPeronWithAsyncThunk.fulfilled]: (state, action) => {
state.loading = false;
state.users = action.payload;
},
},
});
//Component
const MyComponent = () => {
const { users } = useUserList(); //selector
const dispatch = useDispatch();
const getList = async () => {
//await has no effect
await dispatch(fetchPeronWithAsyncThunk({ page: 1, perPage: 10 }));
};
return (
<div>
<button onClick={getList}>Fetch user</button>
<div>{users.length && users.map((user, index) => <div key={index}>{user?.name}</div>)}</div>
</div>
);
};
await at that point has exactly the effect you are waiting for.
But since you are in a closure your state will not update within your getList function.
You can get the result of the thunk in your code though:
const result = await dispatch(fetchPeronWithAsyncThunk({ page: 1, perPage: 10 })).unwrap();
Also, you should be using the builder notation for extraReducers. We will deprecate the object notation you are using soon.

Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules

const accounts = await web3.eth.getAccounts();
App = {
load: async () => {
await App.loadWeb3(
await App.loadAccount()
)
},
loadWeb3: async () => {
if (typeof web3 !== 'undefined') {
App.web3Provider = web3.currentProvider
web3 = new Web3(web3.currentProvider)
} else {
window.alert("Please connect to Metamask.")
}
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum)
try {
// Request account access if needed
await ethereum.enable()
// Acccounts now exposed
web3.eth.sendTransaction({/* ... */})
} catch (error) {
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = web3.currentProvider
window.web3 = new Web3(web3.currentProvider)
// Acccounts always exposed
web3.eth.sendTransaction({/* ... */})
}
// Non-dapp browsers...
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!')
}
},
loadAccount: async () => {
App.account = web3.eth.accounts[0]
console.log(App.account)
}
}
$(() => {
$(window).load(() => {
App.load()
})
})
The error is in LINE 1 where I get the accounts from Ganache but await is valid only for async.
What changes should I make in this code to remove the error? Please help me.
If I remove this line the error says that it cannot access accounts and after this await does not work.
Is there any way to make this piece of code in the form of an ASYNC function?
await calls can only be made in functions marked as async. As you have used await in line 1 it is not wrapped in an async function. You can wrap your code in a async function and then call that function. e.g something like:
const main = async () => { // <- the async wrapper function
const accounts = await web3.eth.getAccounts();
// .... rest of your code
$(() => {
$(window).load(() => {
App.load()
})
})
}
main()
Or if you want to be more advanced and not save the function at all
(async ()=>{
const accounts = await web3.eth.getAccounts();
// .... rest of your code
})() // <- call the function right after declaring it

Uninfinite async function execution in next js

I'm new to next js. And I have one user.js file inside of my pages directory in next.js. This is the source code:
// pages/user.js
function user(props) {
const [userList, setUserList] = useState([])
const [roleList, setRoleList] = useState([])
async function initialFetch() {
const userList = await fetch('GET', GET_ALL_USERS)
setUserList(userList)
const roleList = await fetch('GET', GET_ALL_ROLES)
setRoleList(roleList)
console.log('userList in async')
console.log(userList)
}
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
console.log('userList outside')
console.log(userList)
return (
<>
<TableUserManagement users={userList} roles={roleList}/>
</>
)
};
user.getInitialProps = async (ctx) => {
const userList = await fetch('GET', GET_ALL_USERS)
const roleList = await fetch('GET', GET_ALL_ROLES)
return {userList, roleList}
}
The problem is that above async initialFetch() function is always called uninfinitively :
So what am I doing wrong here? Thank you
Note: I have tried to use useEffect() but the looping still happening. This the code :
useEffect(
() => {
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
console.log('user list diliuar')
console.log(userList)
}
)
This issue is not related to Next.js but React itself. This is the code that cause unfinite calls:
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
Since after setting any state, your component re-renders and that part of code keeps running again, and the fetch cause setting state again,... that loops forever.
You should move you data-fetching logic in side componentDidMount or useEffect. Remember to provide the dependency array of useEffect. In this case, you may only need to fetch data only once so you should provide the empty dependency array.
useEffect(() => {
async function initialFetch() {
const userList = await fetch('GET', GET_ALL_USERS)
setUserList(userList)
const roleList = await fetch('GET', GET_ALL_ROLES)
setRoleList(roleList)
}
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
}, []);
P/s: you should name you React component in PascalCase (ex: User)

Webrtc connect peers on call function

I know this probaly is a longshot, but I was hoping for someone to point me in the right direction here.
I've made a simple peer to peer video connection, and wrapped it inside a function, so I can call it on a button click, but it's not running.
When it's not wrapped inside the "activateVideoStream" function, but just on load, it works fine. I have a feeling that the issue is around the async function, but I can't wrap my head around it.
Here is the code:
let isAlreadyCalling = false;
const remoteVideo = document.getElementById("remote-video");
const {
RTCPeerConnection,
RTCSessionDescription
} = window;
let peerConnection;
function activateVideoStream() {
const configuration = {
"iceServers": [{
"urls": "stun:stun.l.google.com:19302"
}]
};
console.log("Activate Video Stream");
peerConnection = new RTCPeerConnection(configuration);
navigator.getUserMedia({
video: true,
audio: true
},
stream => {
const localVideo = document.getElementById("local-video");
if (localVideo) {
localVideo.srcObject = stream;
}
stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
},
error => {
console.warn(error.message);
}
);
peerConnection.ontrack = function ({
streams: [stream]
}) {
if (remoteVideo) {
remoteVideo.srcObject = stream;
}
};
}
async function callUser(socketId) {
console.log("Call User");
remoteVideo.style.display = "block";
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(new RTCSessionDescription(offer));
socket.emit("callUser", {
offer,
to: socketId
});
}
socket.on("callMade", async data => {
console.log("Call made");
await peerConnection.setRemoteDescription(
new RTCSessionDescription(data.offer)
);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(new RTCSessionDescription(answer));
remoteVideo.style.display = "block";
socket.emit("makeAnswer", {
answer,
to: data.socket
});
});
socket.on("answerMade", async data => {
console.log("Answer made");
await peerConnection.setRemoteDescription(
new RTCSessionDescription(data.answer)
);
if (!isAlreadyCalling) {
callUser(data.socket);
isAlreadyCalling = true;
}
});
I've noticed that "peerConnection.connectionState" inside "callUser" is being set to "new", but without the function wrapped around, it's set to "complete", so that's probaly the issue.

Webdriverio wait until visible

I'm using webdriverio and need to wait for an element to be in the viewport
So I tried
browser.waitUntil(async () => {
const b = await link.isDisplayedInViewport()
return b;
}, 5000, 'expected to be visible after 5s');
But somehow waitUntil wants a boolean not a Promise<boolean>
How can I fix this?
Update:
I use WebdriverIO in a NodeJs app as follows
const { remote } = require('webdriverio');
(async () => {
const browser = await remote({
logLevel: 'error',
path: '/',
capabilities: {
browserName: 'chrome'
}
});
await browser.url('https://example.com');
const link = await browser.$('.special-link');
const ok = await browser.waitUntil(async () => {
const b = await link.isDisplayedInViewport()
return b;
}, 5000, 'expected to be visible after 5s');
await link.click();
const title = await browser.getTitle();
console.log('Title was: ' + title);
await browser.deleteSession();
})().catch((e) => console.error(e));
source
In the options is don't see anything about with or without async/await. Furthermore I don't use a testing framework!
Sorry for the late answer. But if you are still looking for a solution, Please try this if you want to avoid aync/await
get link() { return browser.element('.special-link'); }
browser.waitUntil(() => this.link.isVisibleWithinViewport(), 20000, 'link not visible')
const elementWaitingToBeDisplayed = $("some element locators")
elementWaitingToBeDisplayed.waitForDisplayed(10000, false)
If you set the second argument of elementWaitingToBeDisplayed.waitForDisplayed to true it waits for the opposite.

Resources