11

I need to use some async code in my Postman test.

As it's a complex scenario I've reproduced the scenario in a very simple test with the following code:

let promiseNumber = 0;

function resolvedPromise() {
    return new Promise((resolve, reject) => {
        pm.sendRequest('https://postman-echo.com/get', (err, res) => {
            if (err) {
                console.log(err);
                reject();
            } else {
                console.log(`Resolved promise ${++promiseNumber}`);
                resolve();
            }
        });
    });
}

resolvedPromise()
    .then(resolvedPromise)
    .then(resolvedPromise)
    .catch(err => console.log(err));

The expected result on the console would be:

Resolved promise 1
Resolved promise 2
Resolved promise 3

But instead I receive:

Resolved promise 1

Is there a way to make Promises or async code available at Postman?

Felipe Plets
  • 7,230
  • 3
  • 36
  • 60

2 Answers2

10

UPDATES 27th Feb, 2023:

  • The solution below has been tested in versions from Postman 5.x.x to 10.10.9 and it works as expected.
  • As of Postman v10.6.0 Promises and async-await are fully supported and the workaround below is not needed anymore. Technical details: Postman App PR 4131 Postman Sandbox PR 872

Original Solution (Postman < 10.6)

UPDATE: The original solution used 2147483647 as timeout value. A user suggested using Number.MAX_SAFE_INTEGER in the comments. For me, both worked, but many users reported Number.MAX_SAFE_INTEGER does not work so the code uses the specific number 2147483647.

I realized that it always stops working after I use pm.sendRequest. If I try to resolve the Promise it works.

It seems a known bug looking at this thread.

The workaround for it would be only leave an open timeout while processing the code. Just ensure all possible paths clear the timeout or the call will hang for 300000 years

// This timeout ensures that Postman will not close the connection before completing async tasks.
//  - it must be cleared once all tasks are completed or it will hang
const interval = setTimeout(() => {}, 2147483647);

let promiseNumber = 0;

function resolvedPromise() {
    return new Promise((resolve, reject) => {
        pm.sendRequest('https://postman-echo.com/get', (err, res) => {
            if (err) {
                console.log(err);
                reject();
            } else {
                console.log(`Resolved promise ${++promiseNumber}`);
                resolve();
            }
        });
    });
}

resolvedPromise()
    .then(resolvedPromise)
    .then(resolvedPromise)
    .then(() => clearTimeout(interval))
    .catch(err => {
        console.log(err);
        clearTimeout(interval);
    });

You can check that the promises are resolved by checking the Postman Console (View -> Show Postman Console):

Resolved promise 1
Resolved promise 2
Resolved promise 3
Felipe Plets
  • 7,230
  • 3
  • 36
  • 60
  • 1
    Personally, I woudl use `Number.MAX_SAFE_INTEGER` instead of hardcoded `2147483647`. It's not the same number but it is the _maximum safe integer_. – Elie G. Jul 17 '19 at 18:02
  • 5
    I tried to use it with Number.MAX_SAFE_INTEGER and it didn't work. If you replace it with anything over 1000 it works. – seemcat Oct 09 '20 at 23:51
  • 2
    Each request seems to take somewhere between 200 and 500 ms. So yes, I can confirm the previous comment suggesting _anything over 1000_. The more requests you chain, the higher value you will need for the `clearTimeout`. I also played with the upper limit, and found that 1 billion works, but not 10 billion (ms). – Henke May 17 '21 at 11:06
  • 2
    @seemcat Agree, this is very important! Do NOT use `Number.MAX_SAFE_INTEGER`!! Use something like `100000`. – blackr1234 Aug 02 '21 at 03:57
  • I am confirming that Number.MAX_SAFE_INTEGER does not work on PM (V7.36.7 the last version which plays nice with our corporate firewall). Since the goal is block until the script is done, I choose a meaningful interval, such as // keeps sandbox alive!! const sandbox_stay_alive = setTimeout(() => {}, 60 * 1000); – Andrew Dennison Jan 14 '23 at 17:22
  • Thank you @AndrewDennison, I have updated it mentioning your experience. – Felipe Plets Jan 16 '23 at 16:37
  • wow I can't believe Promises were not supported just as I was thinking it would solve my own prerequest scripting needs. @AndrewDennison however reasonable you think a timeout should last to run the script it would be a good idea to wrap it in a `try catch` block and throw a `request timeout` error to the console – stelloprint Feb 22 '23 at 22:21
  • FYI to everyone looking this thread. At this time it seems like Postman may not support `setTimeout()`, Promises, `async` `await`. However, it seems like at least `setTimeout()` was supported in the past. Without the ability to chain promises and async operations together it is hard to develop meaningful scripts in my opinion: https://github.com/postmanlabs/postman-app-support/issues/11063#issuecomment-1441005273 – stelloprint Feb 23 '23 at 00:27
  • @stelloprint I have just checked on Postman 10.10.9 and the solution above still works as expected. Also since Postman 10.6.0 it fully supports promises and async-await https://github.com/postmanlabs/postman-sandbox/pull/872 and has for a long time kept setTimeout. – Felipe Plets Feb 28 '23 at 01:48
0

In addition to Felipe's answer, I'd like to share a little bit more with my experience in using Postman.

As I need to extract some values from the responses of pm.sendRequest and use them in making the main call (e.g. query string) and/or in the Tests section, I run the script in the Pre-request Script section and set the values in Postman environment variables.

One important point that I find out is that I must put all the code of setting variables (e.g. pm.environment.set(k, v)) before clearTimeout(timeout). Otherwise, if I do it the other way round, the pm.environment.set(k, v) code will still be run but the value of the environment variable will not be updated.


Below is an example with Postman v8.5.1.

Main call

Expect to get TEST from the environment variables.

GET http://google.com/{{TEST}}

Pre-request Script

Expect to set the TEST environment variable of which the value comes from the results of multiple APIs. In this example, I just use the last value returned from Promise.all.

// make sure you do NOT use Number.MAX_SAFE_INTEGER !!
const timeout = setTimeout(() => {}, 100000);

const promise = () => {
    return new Promise((resolve, reject) => {
        console.log('Calling');
        pm.sendRequest('https://jsonplaceholder.typicode.com/todos/' + _.random(1, 100), (err, res) => {
            console.log('run');
            if (err) {
                reject();
            } else {
                resolve(res.json());
            }
        });
    });
}

Promise.all([
    promise(),
    promise(),
    promise(),
    promise(),
    promise(),
    promise(),
    promise(),
    promise(),
]).then(values => {
    console.log('All done');
    const exampleValue = values[values.length-1].id;
    console.log("Last ID: " + exampleValue);

    clearTimeout(timeout);

    // move this line before clearTimeout to fix TEST being undefined
    pm.environment.set("TEST", exampleValue);
});

Tests

Expect to print the TEST environment variable.

// you get undefined if pm.environment.set is run after clearTimeout
// you get correct value if pm.environment.set is run before clearTimeout
console.log(pm.variables.get("TEST"));

How to test

After copying the URL and all the scripts to Postman, open up Console and click Send. Take a look at the query string of the actual URL being called (i.e. GET http://google.com/%7B%7BTEST%7D%7D). Then rearrange the code as mentioned in the comments and click Send again. This time everything should work as expected.

blackr1234
  • 1,420
  • 12
  • 23
  • Hi! I just found myself in a scenario exactly like this one. Tried implementing this solution and even though it works when running from Postman, it doesn't when I execute the tests with newman. My variable (setting it as global, collection, environment or just variable) doesn't get set... Any idea if it could have something to do with Postman version? I am in 9.0.3 (latest) – jprealini Sep 24 '21 at 16:51