0

I am exploring the use of Promise.all(), but I don't know why it doesn't give me expect result. I try to illustrate it step by step.

Let take a look of my code:

var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 2000, 'foo');
});


var apiCall = async () =>{
    // to simulate a api call that will response after 5 sec 
     setTimeout(() => {return 1000}, 5000); 
    
}

Promise.all([p2,p3,apiCall()]).then(values => {
  console.log(values); // [3, 1337, undefine], but I expect  [3, 1337, 1000]
});

apiCall().then((response)=>{console.log(response)})

As my understanding, async function will immediately return a Promise, which is what Promise.all will wait for.

So I expect,

.then(values => {
  console.log(values); // [3, 1337, undefined]
});

will only execute after 5 sec.

But the output is like below in 2 sec already, and not [3, 1337, 1000]

undefined   
[ 1337, 'foo', undefined ]

I dont know where the problem lies, I expect

apiCall().then((response)=>{console.log(response)})

will give me "1000" instead of undefined


new edit

After gathering you guys answers, I tried this.

As my understanding, setTimeout is also a async, and it will automatically return a promise like any other promise.

So, based on this understanding, I write below code. but it doesnt work. I understand using Promise constructor will fix the problem. But I dont know what problem lies in this example

var apiCall = async () =>{
    // to simulate a api call that will response after 5 sec 
     const a = setTimeout(() => {return 1000}, 5000); 
    return a 
}
Ae Leung
  • 300
  • 1
  • 12
  • 1
    It's not `Promise.all()` - it's `apiCall()` which doesn't correctly return a value after some time. – VLAZ Nov 29 '22 at 09:29
  • 2
    This has nothing to do with Promises. `return` is meaningless in a `setTimeout` callback. – Sebastian Simon Nov 29 '22 at 09:29
  • May I know how to fix it? – Ae Leung Nov 29 '22 at 09:29
  • let assume it is a api call situation – Ae Leung Nov 29 '22 at 09:30
  • 1
    @AeLeung You’ll have to use a `Promise` constructor; see [What is the JavaScript version of sleep()?](/a/40567028/4642212). You can pass a value to `resolve` using the third argument of `setTimeout`: `setTimeuot(resolve, 5000, 1000);`. – Sebastian Simon Nov 29 '22 at 09:31
  • 1
    "*`apiCall().then((response)=>{console.log(response)})` will give me "1000" instead of undefined*" [I assure you, it doesn't](https://jsbin.com/powamid/edit?js,console). "*how to fix it?*" is in conflict with "*let assume it is a api call situation*" - if you have a real proper async function, then it must work. Fixing this faulty function will not solve your real issue. – VLAZ Nov 29 '22 at 09:32
  • @VLAZ exactly, I just assume why real api call would work in this similar context. Then I try to imatie it by writing this apiCall().then((response)=>{console.log(response)}) . So, what real api call does to make apiCall().then((response)=>{console.log(response)}) work? (I know it doesnt work in this example, but you know what I meant) – Ae Leung Nov 29 '22 at 09:36
  • 1
    What it does is return a proper promise that resolves when the async operation finishes. Doesn't return immediately but also start a timer which fires in the future, yet is completely unconnected to the promise that was already returned and resolved. – VLAZ Nov 29 '22 at 09:39
  • I see, it is because the promise returned has nothing to do with my async opertaion, it just returned and resolved with undefined, while in real api call situation, the promise returned is still connected to the operation and its resolve is still controlled by the operation. So that's why the .then() is only being executed when the response finished, while my example here, .then() run immediately even though the meaningful operation haven't started yet. right? – Ae Leung Nov 29 '22 at 09:47
  • 1
    @AeLeung _“the promise returned is still connected to the operation and its resolve is still controlled by the operation”_ — This sounds too much like magic. What’s _really_ happening is that the Promise chain only works (i.e. `await x` actually resulting in a value or `.then((x) => { x; })` actually receiving a value) when Promises are _returned_ at the appropriate places, or when values are properly _resolved_. Your `apiCall` async function, for example, returns nothing and includes no `await`s, so it resolves immediately with `undefined`. The `return` is in an entirely different function. – Sebastian Simon Nov 29 '22 at 09:57
  • 1
    _“new edit”_ — `return a` won’t return `1000` (at least not deliberately). Check the [documentation](//developer.mozilla.org/en/docs/Web/API/setTimeout) what `setTimeout` _returns_: it’s a number identifying the given timeout, so it can be stopped with `clearTimeout`. Since this is a `return` in your `async` function, `apiCall` returns a Promise that resolves to this identifier. It has nothing to do with the `return 1000;` inside. Again: `return` inside the `setTimeout` callback is meaningless and will be ignored. – Sebastian Simon Nov 29 '22 at 10:01

3 Answers3

5

Your api call is not returning the value properly. Try by returning a promise which resolves after the timeout

var apiCall = async () =>{
  // to simulate a api call that will response after 5 sec 
  return new Promise(resolve => {
    setTimeout(() => {resolve(1000)}, 5000); 
  });
}
Xiduzo
  • 897
  • 8
  • 24
  • Great example! Could you also upvote my question? no sure why I cant accpet your answer right now, I will come back and check again – Ae Leung Nov 29 '22 at 10:25
  • As promised, I tried to accepted your answer today and seems stackover could only accept one answer in this post, even though I knew it could accept multiple answers in the past. I still upvoted your answer. Thanks for your help in this question. appreciated it. @Xiduzo – Ae Leung Nov 30 '22 at 03:14
  • 1
    @AeLeung You were never able to accept more than one answer on Stack Overflow. – Sebastian Simon Nov 30 '22 at 03:25
  • @SebastianSimon , really ? I don't know why I have impression that I have seen multiple accepted answers from somewhere or I have accepted multiple accepted answers before. – Ae Leung Nov 30 '22 at 03:55
4

In short, you haven't done anything that would make apiCall wait for the timeout before resolving the promise.

var apiCall = async () =>{
    // to simulate a api call that will response after 5 sec 
     setTimeout(() => {return 1000}, 5000);    
}
  1. This is an async function so it return a promise.
  2. The first thing it does is call setTimeout
  3. Then it gets to the end without hitting a return statement so resolves the promise as undefined
  4. 5 seconds later the timeout finishes and calls the function passed to setTimeout. This function returns 1000. setTimeout doesn't do anything with the return value from the callback.

To get a promise from a callback API (like setTimeout) you need to use new Promise (as you did for p3).

The async keyword is only useful in that it allows you to use the await keyword. The fact it makes a the function return a promise should be thought of as a side effect needed to make await work.

await is a tool for managing existing promises.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • May I know where will that 1000 return to and where to access it? say it is a api call will reutrn a result after certain secs , and I have to access it – Ae Leung Nov 29 '22 at 09:32
  • The callback returns to the internals of `setTimeout`. You cannot access it because `setTimeout` doesn't do anything with it. I don't know what "it" you are reading but it is either wrong or you are misinterpreting it. – Quentin Nov 29 '22 at 09:33
  • To get a value from `setTimeout` you need to (as per the bold text in this answer) use `new Promise` and call `resolve` with the value you want as an argument. – Quentin Nov 29 '22 at 09:36
  • May I know if using new Promise would cause a problem called anit-pattern? coz I have tried to use new promise constructor to fix this problem. But some code reviewer said it is anti-pattern code, and shouldn't write it. But only writing .then really couldnt achieve what I want – Ae Leung Nov 29 '22 at 09:40
  • To do what you are trying to do here, you need a Promise constructor. Used correctly it won't be an anti-pattern. I can't tell if it would be an anti-pattern when you implement it because (a) I haven't seen your attempt and (b) Your question is high abstract (returning fixed numbers after specific amounts of time doesn't strike me as likely to be your real problem). – Quentin Nov 29 '22 at 09:42
  • 2
    @AeLeung You’re referring to the [explicit Promise construction anti-pattern](/q/23803743/4642212). This only applies when you _explicitly_ use a constructor invocation like `new Promise` in cases where a Promise is _already, naturally_ provided in some way. For example, `async (x) => new Promise((resolve) => resolve(x))`. Many modern APIs utilize Promises under the hood, so constructing one is rarely needed. `setTimeout` does _not_ belong to these modern APIs. Related: [How do I convert an existing callback API to promises?](/q/22519784/4642212). – Sebastian Simon Nov 29 '22 at 09:50
  • @Quentin Since the problem is the returned promise, and not resolved proberly So, I tried a new approach. but it doesnt work. Could you see why (in the new edit) – Ae Leung Nov 29 '22 at 10:03
  • 2
    The return value of `setTimeout` is a number for use with `clearTimeout`. As previously mentioned: **You need to use `new Promise` if you want to convert `setTimeout` into a promise** – Quentin Nov 29 '22 at 10:04
  • I see. another question I would like to ask is that if async function will immediately return a promise when it is being called, and it will be resolved with the line hitting the return? Can you confirm with it ? – Ae Leung Nov 29 '22 at 10:06
  • Please see the numbered list in the answer where I went through how the function works in order. The list explicitly answers that question. – Quentin Nov 29 '22 at 10:08
  • @Quentin I got that actually, I just have no confidence of what I understood. But thanks a lot – Ae Leung Nov 29 '22 at 10:12
  • @SebastianSimon async (x) => new Promise((resolve) => resolve(x)). IS wrong because it could be written in this way right : async (x) => return x – Ae Leung Nov 29 '22 at 10:12
  • @AeLeung — What does that have to do with anything? There's no `setTimeout` there. – Quentin Nov 29 '22 at 10:14
  • @Quentin , it is just that in SebastianSimon's example mentioning that async (x) => new Promise((resolve) => resolve(x)) ; So I just want to confirm what he meant – Ae Leung Nov 29 '22 at 10:19
  • 1
    @AeLeung Almost: `async (x) => x`, sans `return`. Other than that: yes, correct. – Sebastian Simon Nov 29 '22 at 10:24
1

You are not returning anything in the apiCall. When using setTimeout you specify a callback function which will be executed after the given time. What you return in the callback basically doesn't matter.

You should make the apiCall function to return a new Promise which resolves inside the setTimeout callback.

Loránd Péter
  • 184
  • 1
  • 5