5

I want to return the status and store its value in variable s. I will appreciate some help please.

here is my code:

let s = setTimeout( ()=>{
   this.matchService.getMatches().subscribe(ms => {
       this.matches = ms;
       let match = this.matches.find(match => match.id == id);
       let status = match.status;
       if(status == 'closed' || status == 'live') {
          this.status.name = status;
        }
        return status;
      });
  },9000);
}
yosra
  • 702
  • 1
  • 11
  • 24
  • 4
    Well `setTimeout()` does return a value, that being the identity of the timer itself. You cannot return anything from the callback; it doesn't make sense. – Pointy Apr 18 '19 at 13:21
  • What happens to s? What is inside after running this ? – srknzl Apr 18 '19 at 13:21
  • What's the reason behind this timeout? why is it needed? – briosheje Apr 18 '19 at 13:23
  • @Pointy so is there any way I can return the status varibale? – yosra Apr 18 '19 at 13:24
  • @user11290658 just store it in the component, since you're using angular. What is it for anyway? can you please provide more code to work with? you're likely following the wrong approach, to me. – briosheje Apr 18 '19 at 13:24
  • @user11290658 no. The call to `setTimeout()` returns immediately; the surrounding code does not wait for the timer. – Pointy Apr 18 '19 at 13:24
  • this timeout will wait for 9s before checking if the field status on my database was modified, if it was modified then I have to set it to a particular value – yosra Apr 18 '19 at 13:25
  • @user11290658 what happens if the value changes after 9 seconds? that's what I'm wondering. An interval sounds rather more suitable here. – briosheje Apr 18 '19 at 13:26
  • @Pointy Unless you are able to use async and wrap it in a promise, which does give you the appearance of waiting – Ruan Mendes Apr 18 '19 at 13:29

2 Answers2

8

This answer here is specifically for the setTimeout question. If you work with an observable, consider the answer of bambam!

Ok, this answer might be a little weird if you don't know the concept of async. Basically the easiest is to wrap your setTimeout into a Promise, like so:

const someTimeoutAction = () => {
  // Let's return a new Promise, promising to eventually return a value
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('hello world');
    }, 1000);
  });
};

// The following function is an async function, that will
// help to work better with Promises.
const mainFunction = async () => {
  console.log('Waiting for status');
  const status = await someTimeoutAction();
  console.log(status);
};
mainFunction();

So what happens here?

  1. The mainFunction is called and calls someTimeoutAction.
  2. The someTimeoutAction returns a promise. The old syntax looks a bit different. This medium article document should be a good starting point.
  3. mainFunction waits for the Promise to resolve. After a second it is resolved and the value is written to status.
  4. Everything else just continues now like usual.

The code above only works for modern browsers. It does not work without a transpiler for e.g. IE11. However, the old syntax works just fine:

function someTimeoutAction() {
  // Let's return a new Promise, promising to eventually return a value
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('hello world');
    }, 1000);
  });
};

// The following function is an async function, that will
// help to work better with Promises.
function mainFunction() {
  console.log('Waiting for status');
  someTimeoutAction()
    .then(function(status) {
      console.log(status);
    });
};
mainFunction();
lumio
  • 7,428
  • 4
  • 40
  • 56
5

Since you already have an observable, simply delay it instead of using setTimeout! Also the promise approach from the other answer is nonsense for this scenario.

this.matchService.getMatches()
    .pipe(delay(9000))
    .subscribe(ms => {
       this.matches = ms;
       let match = this.matches.find(match => match.id == id);
       let status = match.status;
       if(status == 'closed' || status == 'live') {
          this.status.name = status;
       }
  });

The bigger problem with your code is that you would never never return from subscribe. Actually you would rather delay the (I'm guessing) http call in matchService, but you didn't show the relevant code.

Note that a subscription may get triggered multiple times, again depending on what getMatches() is. You're on the wrong path here and should update your question so we can tailor you a real solution.

baao
  • 71,625
  • 17
  • 143
  • 203
  • I'm still wondering why a timeout is used instead of an interval. If the goal is to update something when a server-side value changes, a timeout doesn't really make much sense at all. – briosheje Apr 18 '19 at 13:34
  • You can just return a promise and combine it with async `return this.matchService.getMatches().pipe(delay(9000)).toPromise().then((v)=>v.something)` – Ruan Mendes Apr 18 '19 at 13:34
  • No @JuanMendes, that's not needed. Not at all really. – baao Apr 18 '19 at 13:35
  • 1
    @JuanMendes that's angular, why would a promise be needed here? The timeout approach with the promise, to me, in that context is just conceptually wrong, with a framework like angular. I'm not saying it doesn't work, it's just weird. – briosheje Apr 18 '19 at 13:35
  • @bambam Then what does returning status from your subscribe handler do? Nothing? It does something in my case, right? Isn't that what the OP was trying to do: pass status back to the caller? – Ruan Mendes Apr 18 '19 at 13:41
  • I've deleted the return line from the code in the answer, since you would never do that. @JuanMendes Yes, it does something in your case, but something what is a complete antipattern when working with observables. – baao Apr 18 '19 at 13:43
  • @briosheje i actually think it's silly to work with observables that only fire once, like HttpClient, so I convert them to promises, unless I'm actually streaming something. I'm just saying so it can do what the OP asked – Ruan Mendes Apr 18 '19 at 13:43
  • 2
    @JuanMendes The question is about updating a flag when a database value changes, it doesn't sound like Promise task to me. Again, I'm not saying it's wrong, I'm just saying that it's weird by design. Adding a Promise won't solve the issue, it will just add more code that it's really unneeded, since the result can be binded to angular property directly without the need of a Promise or anything. It's just that the design chosen by the OP is wrong, because the task of checking if a value changes cannot be done with a timeout and, in angular, this **shouldn't** be done by the component. – briosheje Apr 18 '19 at 13:46
  • I guess I'm not trying to guess what the OP's code is doing, I'm just trying to answer the question. It looks like you can see deeper into the OP's motivation, but you have no guarantee that `getMatches` actually fires multiple events – Ruan Mendes Apr 18 '19 at 13:51
  • @JuanMendes In fact, it **currently doesn't**, since it's using a timeout. The correct approach from a design point of view is that **the service** should fire that observable **whenever** that database value changes, by either triggering a request every X seconds to the server, either by using a web socket or something similar, that would be the correct approach. To solve th op issue, to me, neither a Promise nor altering the observable is needed.by binding the `status` value to the component, you can use it in the view regularly, regardless `undefined` or with a value and can act accordingly – briosheje Apr 18 '19 at 13:54
  • Once again, I wouldn't say that the promise approach is wrong, it's just unnecessary **with angular**, while it would be perfectly fine **without** angular and observables. Don't get me wrong for that, I'm just talking about design, I did upvote both answers, because I think both are corrects and, at the same time, wrong, since none of these, to me, answers the question, which is still unclear to me, since the OP didn't explain why he needs the timeout :P – briosheje Apr 18 '19 at 13:56
  • Again, I'm not trying to fix the design, you don't have to teach me, I know exactly what you are talking about. However, I have no clue what this is for. The question, as posted, is clearly asking for passing back status within a timeout . `I want to return the status and store its value in variable s.` That's why it's been closed as a duplicate – Ruan Mendes Apr 18 '19 at 13:56
  • @briosheje I've used the timeout just for testing – yosra Apr 18 '19 at 14:15
  • @user11290658 if you want to use an interval instead, I would recommend you to move the logic to **the service** instead and use the observable to actually be alerted whenever that value changed. – briosheje Apr 18 '19 at 14:17
  • @briosheje actually the service fires an obvervable, and the database gets updated whenever the form is submitted, the problem appears when we update manually the database while filling the form (in this case once we submitted the form the database will change again which we want to avoid) – yosra Apr 18 '19 at 14:20
  • 1
    @user11290658 by design (beware, I'm talking about **design** here), the **component** should **not** perpertually call a service. The service should instead **notify** the component whenever a value change, and the component should **act** when this happens. The service, in that case, should have an interval and actually call the server and should **emit** a value whenever a condition is met. If that's the case, please update your example so that we can properly help you. – briosheje Apr 18 '19 at 14:23