Click here to Skip to main content
14,928,677 members
Articles / Programming Languages / Javascript
Tip/Trick
Posted 25 Apr 2021

Tagged as

Stats

10.2K views
7 bookmarked

Unexpected Side Effect from async

Rate me:
Please Sign up or sign in to vote.
3.95/5 (9 votes)
28 May 2021CPOL3 min read
Prepending the signature of a function with async produces a brand new Promise with potentially and unexpected side effect
The keyword async always creates a brand new Promise even if the function it prepends returns its own Promise. That leads to potentially and unexpected side effect (maybe also a memory leakage but that is still to prove and any help to verify that is welcome)

Introduction

  • The Promise object obtained from a function marked with the async keywords is always a brand new Promise (If the function returns a promise, it will be replaced by the new Promise).  
  • The Promise instantiated in the body of the function is not disposed. (Could we say that async is prone to memory leakage? This statement is still an open question and needs the help of some of you who can give the right answer.)

Background

Basic knowledge of Javascript.

Using the Code

(The code shown here can be copied and pasted directly in any editor and easily checked. The not build in function isPromise is defined at the end of this tip)

Everyone knows that a function marked with the async keyword always returns a promise object. So here comes the question:

Is it correct to return a Promise from a function marked by async?

I mean is this function correctly defined?

JavaScript
async function amICorrect(){
    // your implementation here
    return new Promise((resolve,reject) => {});
}

To find an empirical answer, we are going to use some little code snippets.

Step 1

This function is useless, but formally correct.

JavaScript
var s = testS(){};
console.log("Does the function 'testS' return a 'Promise'?", isPromise(s));

In this case, isPromise returns false and that is correct: testS doesn't return a Promise.

Prepending the function with async, the same signature will now return a Promise object.

JavaScript
async function testA(){}
var a = testA();
console.log("Does the function 'testA' return a 'Promise'?", isPromise(a));

Now, isPromise returns true. That because async wraps the return value (void in this case) into a Promise;

So far so good. Now it is time to dive into the problem.

Step 2

This function adds the dynamic property check to a new Promise and returns the promise.

JavaScript
function checkS(){

    var p = new Promise((resolve,reject) => {});
    p.check = 'check';
    return p;
}

var promiseS = checkS();
console.log("Is the property 'check' in the variable 'promiseS'? ", ('check' in promiseS));

(check in promiseS) should be true! And it is!

We proved here that the returning value is exactly the Promise object we instantiate within the function. No one should be surprised: in the end, that's what we expect from a function.

Step 3

We know async wraps the returning value into a Promise. What happens if the return value is already a Promise? (Will the async function be smart enough to understand the function is already returning a Promise and then return it instead of a new Promise?)

JavaScript
async function checkA(){
    return await checkS();
}
var promiseA = checkA();
console.log("Is the property 'check' in the variable 'promiseA'? ", ('check' in promiseA));

(check in promiseA) should be true! But it isn't! The dynamic property check is gone!

There is another final check we can do.

Step 4

JavaScript
let globalP;
async function checkLA(){
    var p = checkS();
    globalP = p;
}
var promiseLA = checkLA();
console.log(globalP);

This snippet checks if the variable globalP still keeps a reference to the instance of the Promise, created in the function checkS. It does!

The promise created in checkS is still alive which means: async does not dispose the original Promise object.

So the lesson is async is not that smart, the developer must be aware of this.

So, as a rule of thumb, we can say:

  1. Prepend a function with the keyword async only if you want to use await in it.
  2. Returning a Promise from an async function will return a brand new Promise.
  3. 'async' does not dispose the promise object created in the body of the function.

Point 3 raises a new question about memory leakage, if async returns a new Promise object, what about the original Promise returned by the function. Are we in a case of memory leakage? This question is still open!

The function used to check if a variable is a Promise:

JavaScript
function isPromise(value){
    return (!value) ? false : (Object.prototype.toString.call(value) === "[object Promise]");
}

Points of Interest

This code was a post in StackOverflow in the form of a question because, at that time, it was really a question.

People stated that the leakage is not a problem and that the async keyword returns a Promise when the function doesn't return a promise itself.

Since I was not happy with the answers I got (they were statements without any kind of proof), I decided to find the true answer by myself (Steps 2 and 3. I don't know why but less than one day after, the post was hidden.).

So, I think this post shows two points of interest:

  1. Be aware of the real behavior of the 'async' keyword' we discovered here with practical examples.
  2. Don't trust people giving an answer to your question without giving any kind of evidence.

Thanks for your time!

Hope this article helps somehow.

Cheers!

History

  • 25th April 2021: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

CodeErgoSum
Engineer
United Kingdom United Kingdom
I like coding. GitHub is the repository for my projects and CodeProject is where to talk about them.
My big dream is to 'join' some companies together to share the effort of coding common projects

Comments and Discussions

 
Questionplease tag your article with appropriate labels Pin
BillWoodruff28-Apr-21 3:26
mveBillWoodruff28-Apr-21 3:26 
If this is only about JavaScript, indicate that. thanks, Bill
«One day it will have to be officially admitted that what we have christened reality is an even greater illusion than the world of dreams.» Salvador Dali

Questiondisposal is an effect of garbage collection Pin
Pete Lomax Member 1066450527-Apr-21 1:01
professionalPete Lomax Member 1066450527-Apr-21 1:01 
AnswerRe: disposal is an effect of garbage collection Pin
Andre_Prellwitz28-Apr-21 5:05
MemberAndre_Prellwitz28-Apr-21 5:05 
GeneralRe: disposal is an effect of garbage collection Pin
Pete Lomax Member 1066450528-Apr-21 9:09
professionalPete Lomax Member 1066450528-Apr-21 9:09 
AnswerRe: disposal is an effect of garbage collection Pin
CodeErgoSum2-May-21 7:56
MemberCodeErgoSum2-May-21 7:56 
Suggestioncorrect use of async/await Pin
Gábor Angyal26-Apr-21 0:15
professionalGábor Angyal26-Apr-21 0:15 
GeneralRe: correct use of async/await Pin
CodeErgoSum26-Apr-21 1:50
MemberCodeErgoSum26-Apr-21 1:50 
GeneralMy vote of 5 Pin
LightTempler25-Apr-21 4:23
MemberLightTempler25-Apr-21 4:23 
GeneralRe: My vote of 5 Pin
CodeErgoSum26-Apr-21 2:06
MemberCodeErgoSum26-Apr-21 2:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.