Collection variables don't want to be set after an `await` call it seems

Greetings!

I have learned a lot about how awesome Postman is over the past few days, however, I am now stuck with an issue I can not resolve. I would greatly appreciate a nudge in the right direction?

Setup:
In a Pre-request Script I have a call to a function that returns a Promise wrapped around a pm.sendRequest. In the result I receive the response object, pull a value off and attempt to store it in a collection variable. In the Request URI I then echo/use said collection variable.

Problem:
The collection variable just won’t save (well, if I do a .get straight after the .set it is there). I’ve added in console.logs to try and see whether everything is happening in order and it appears that it is. However, the Request URI still doesn’t contain/echo the collection variable specified.

Code:
The Pre-request Script…

(async () => {
    try {
        const subscription = await utils.createSubscription();
        pm.environment.set('subscription_id', subscription.id);
    } catch (error) {
        console.error(error);
    }
})();

The createSubscription function…

const createSubscription = () => {
        return new Promise((resolve, reject) => {
            pm.sendRequest({
                url: `${api_endpoint}/subscriptions`,
                method: 'POST',
                header: {
                    'Content-Type': 'application/json',
                    Authorization: pm.globals.get('jwt_token'),
                },
                body: {
                    mode: 'raw',
                    raw: JSON.stringify({
                        param1: "123",
                        param2: "123",
                        param3: "123"
                    })
                }
            }, (err, res) => {
                if (err) reject(err);
                else {
                        resolve(res.json().id);
                    }
                }
            });
        });
    };

Could it be a scope issue? I have passed pm into the anonymous function with no luck. Is it an async timing issue?

Thank you for your consideration :slight_smile:

Hi @lukefilewalker ,
I tried the exact same thing that you described in your setup, here’s a working solution - https://www.postman.com/mdhananii/workspace/meena-s-public-workspace/request/582779-e567ca70-b911-46d6-84ca-23d37d209f81

What did I learn? :nerd_face:

  • In the first part where you use the async function and call it, apparently, I received an error that said I’m not allowed to do it directly, so I have extracted that bit into a variable and then called the function.
  • In case you try to log something right after you call the async function, you might want to check the sequence in which the calls get executed since it’s async.
  • If you’re looking for the variable that you set programmatically to appear in the environments UI, I don’t see that happening, but I’m still able to do a get call on the variable right after setting it.

Please let me know if the solution in my workspace helps. :crossed_fingers: Feel free to fork it :smiley:

P.S: Sharing a solution using a public workspace was @kitty-garrison 's idea.

2 Likes

Hello,

I also encountered this issue. The sendRequest function only supporting callbacks is tough to handle/deal with, so I wrote it around a promise wrapper so I can write asynchronous functions.

As per…

If you’re looking for the variable that you set programmatically to appear in the environments UI, I don’t see that happening, but I’m still able to do a get call on the variable right after setting it.

Wouldn’t that be a bug that needs to be reported and addressed then? I have been having issues where if I try to set anything inside of an async function with pm.environment, pm.request, etc. ,it does not actually set data in postman or to the request after the pre-script finishes.

Hey @jgrubb ,
When you set something, is it not able to fetch the value you set using the get method?
The public workspace solution(navigate to the Pre-request script*) I shared is able to get the value that is set even though we’re doing it as part of an async function. This is how the snippet in the pre-request script looks:

var raw = "This is expected to be sent back as part of response body.";

var requestOptions = {
  url: 'https://postman-echo.com/post',  
  method: 'POST',
  body: raw,
};

const createPromise = new Promise((resolve, reject) => {
    pm.sendRequest(requestOptions, (err, response) => {
        if(err)
            reject(err);
        else { 
            resolve(response.json().data);
        }
    })
})

const setVariable = (async () => {
    try {
        const val = await createPromise;
        pm.globals.set('subscription_id', val);
        console.log(pm.variables.get("subscription_id"));
    } catch (error) {
        console.error(error);
    }
});


setVariable();
console.log("Test message to check sequence of calls")

Ya you can set it and get it with in the prescript, but it does not update the environment variable in the ui. When i rewrote my code using callbacks, it was able to set environment variables in the ui.

So my expectation is…
if I am setting an environment variable within a prescript, it should show that change in the ui. Using callbacks, you get that. With async functions you do not.

My use case is we need to get an auth token and a csrf token before every request. Both of those tokens need to persist in between requests so we are not spamming our auth service by creating a new auth token on every request. So we need to persist the tokens into the environment variable to reuse them.

Here is a trivial example…

// simple wrapper for setTimeout to use inside async function
asyncTimeout = (x) => new Promise((resolve) => setTimeout(resolve, x));

(async() => {
    await asyncTimeout(1000);
    pm.environment.set('async', 'asdf');
    console.log('async', pm.environment.get('async', 'asdf'));
})();

pm.environment.set('outside', 'asdf');
console.log('outside', pm.environment.get('outside', 'asdf'));

Here are screenshots of the console and the environment variables in my ui. As you can see, only the outside environment variable was set. The async environment variable was not set.


Just confirming, so you need to persist variables across requests and do so in an async fashion not using the callbacks, right?

I expect that whenever I call something like…

pm.environment.set

That it always sets the environment variable in the ui. In the example shown above, it does not set the environment variable inside an async function.

Hey @jgrubb,
This is a similar request on Github - Support for Promises & Async / Await in scripts · Issue #4131 · postmanlabs/postman-app-support · GitHub. I’ve added a link to our conversation here on the thread, but feel free to post your feedback, subscribe and upvote the issue.

2 Likes
const createSubscription = () => {

    return new Promise((resolve, reject) => {

        pm.sendRequest({

            url: `https://postman-echo.com/get?foo=bar1`,

            method: 'GET'

        }, (err, res) => {

            if (err) { reject(err) }

            else {

                resolve(res.json().args.foo);

            }

        }

        )

    })

};

(async () => {

    try {

        const subscription = await createSubscription();

        pm.environment.set("id",5)

    

    } catch (error) {

        console.error(error);

    }

})()

setTimeout(()=>{},1000)

just add setTImeout at the end

Nice catch right there @praveendvd!
The thing is that Postman doesn’t wait for the async function to end before it sends the actual request.

This is how I’ve managed to make it work (using @meenakshi.dhanani code, but adding subscription_id to the query params using its global env):

var raw = "This is expected to be sent back as part of response body.";
const _dummy = setInterval(() => {}, 300000);

var requestOptions = {
  url: 'https://postman-echo.com/post',  
  method: 'POST',
  body: raw,
};

const createPromise = new Promise((resolve, reject) => {
  pm.sendRequest(requestOptions, (err, response) => {
    if(err)
      reject(err);
    else { 
      resolve(response.json().data);
    }
  })
})

const setVariable = (async () => {
  try {
    const val = await createPromise;
    pm.globals.set('subscription_id', val);
    console.log(pm.variables.get("subscription_id"));

    clearInterval(_dummy);
  } catch (error) {
    console.error(error);
  }
});


setVariable();
console.log("Test message to check sequence of calls")

I just’ve added a setInterval at the start of the script to keep it alive, and at the end of the async function I’ve done a clearInterval to release it.

Before setInterval/clearInterval:
Screenshot from 2022-02-08 11-36-47

After setInterval/clearInterval: