Hello everyone,
I'm trying to wrap my head around how the vanilla MVC should be done right, and existing resourses have been of great help. Now I need help with something I can't seem to find - or formulate a proper query for.
Here's the relevant part of the code that I have:
//view
class CollectorView{
constructor(){
this.element1 = document.getElementById('collections');
this.element2 = document.getElementById('collections__photos');
}
...
returnCollectionsId(e){
return new Promise((resolve) =>
this.element1.addEventListener('click', e =>
(!e.target.id || e.target.id === 'collections') ? null : resolve(e.target.id)
)
)
}
async renderPhotos(viewModel){
this.element2.innerHTML = viewModel.map(el => `<div>
<h3>${el.description?el.description:el.alt_description}</h3>
<a href="#photos__details" class="circle" id="${viewModel.indexOf(el)}"></a>
</div>`)
.join("");
}
}
//controller
class CollectorController{
constructor(model,view){
this.model = model
this.view = view
this.getId()
}
...
async getId(){
let photosId = await this.view.returnCollectionsId()
let photosPromise = this.model.sendPhotos(photosId)
let photosArray = await photosPromise.then(rsp => rsp.data)
this.view.renderPhotos(photosArray)
}
}
So what happens is: there's a click eventListener that, when a certain element is clicked, reads the id of the clicked element, sends it into the model and does an API call with it, receives the info and renders it.
While this works perfectly, there are two big problems:
1) Promise only runs once. When it resolves with an existing ID, I can't go to the previous view and click a different element - it would have already run and still renders the first thing clicked.
2) The await part of the async/await only will work as intended when it receives a Promise. So can't just pass an ID as a string/number - returns undefined and everything breaks.
So, is there an alternative that would also return a promise, but would work anew every time upon a click happening? Or something to replace async/await with?
Thank you in advance!
Top comments (7)
You can not reuse the promise to go back to the Pending state, but you can create a new one. You can write another generator function to get a new promise on the Fly.
And to deal with returning undefined in async/await; you can do the following:
What
Promise.resolve
does is that it converts a non-promise value given to it to a Promisified value and hence you can await on it; and if the value passed toPromise.resolve
is already a promise, i.e. then-able value; it will continue working as a regular valueYes, thank you, this is exactly what I need!
One question, though... how would I write a generator function for Promises?
Search returns nothing, besides information on the Promises themselves.
Shouldn't my existing returnCollectionsId() already work as a generator? From how I see it, it should return a new Promise everytime it is called, but it doesn't. So that's the part I don't get.
Just send an article my way that explains the topic further. Can't find one :\ Thank you again!
You can use Rxjs Subject and subscribe to it.
const sub = new Subject()
// Inside Event Listener Callback
sub.next(e.target.id)
// Outside or wherever you want
sub.subscribe( id => {
// add your stuff here
})
.subscribe will fire everytime button is clicked or event is fired.
Why are you using promises and async/await, though? As far as I can tell you aren't doing any asynchronous operation. Have you tried just using synchronous code for everything?
I am, probably didn't describe it too well.
There's a model block above view that isn't included, it's responsible for API calls.
The current flow is:
If I do everything synchronously, sendPhotos() model function will return an undefined error. There's only an axios call in it, and it has to wait for the ID to be defined first. Making this function async/await doesn't work, tried.
All I really wanted to know is if there's an alternative for Promises, that can be re-resolved, and searching for one hasn't given any good results. I did find a thing called Observables, but it was reserved for Angular only, and I'm making the app in vanilla JS.
Or, a way to hold await until an element is clicked and its ID is sent to it. But await only works that way with Promises, so not much to do there.
I might be wrong, but I think one way would be to pass a callback into
returnCollectionsId
and then call that callback from insidereturnCollectionsId
from the click event handler you set up. Then anytime element1 is clicked, your callback will be called, instead of just the first time.If you wanted to use Observables I think you could use Rx.js without angular. Instead of returning a promise that sets up an on click handler and resolves when the onClick is called for the first time, you can return an Observable and subscribe to it in whatever code calls
returnCollectionsId
.Here's a blog post about setting up Rx.js with click events from a few years ago codyburleson.com/respond-to-button...
Thank you, I've tried both. The way I made everything, it doesn't want to wait for anything else but a Promise, unfortunately. Still launches immediately. Or I need a couple more days for the new information to sink in, and then I'll figure out how to get it to work. Will see, thank you again!