skip to Main Content

Consider the following code, which awaits a Promise:

async function handleSubmit() {
    try {
        await submitForm(answer);
    } catch (err) {
        console.log('Error')
    }
}

function submitForm(answer) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (answer !== 'OK') {
                reject(new Error('Rejected'));
            } else {
                resolve();
            }
        }, 1500);
    });
}

Note that I never explicitly passed resolve/reject to the Promise, yet they are being passed (breakpoint in any JS console to see for yourselves). So my questions are:

  1. Who passes the parameters to the Promises’ anonymous function? It is the await construct?
  2. What exactly gets passed?

4

Answers


  1. When you create a new Promise, the Promise constructor automatically provides the resolve and reject functions to the executor function. These functions are used to settle the promise

    Basically
    resolve will mark the promise as fulfilled and provide the value whereas reject will mark the promise as rejected and provides a reason (typically an error)

    Login or Signup to reply.
  2. The function you pass to the Promise constructor is executed synchronously, not when you await the promise or call one of its methods e.g. .then(). resolve and reject are passed by the constructor.

    Login or Signup to reply.
  3. Regarding your first question (in its original and updated versions):

    What gets passed to the Promise as the resolve/reject functions?

    Who passes the parameters to the Promises’ anonymous function? It is the await construct?

    What exactly gets passed?

    These functions are created by the JS engine. The specifications can be found in section 27.2.1.3 CreateResolvingFunctions(promise) of the ECMA Script specification. Namely in step 4 and step 9 of that procedure they are created:

    1. Let resolve be CreateBuiltinFunction(stepsResolve, lengthResolve, "", « [[Promise]], [[AlreadyResolved]] »).

    […]

    1. Let reject be CreateBuiltinFunction(stepsReject, lengthReject, "", « [[Promise]], [[AlreadyResolved]] »).

    The above procedure is executed when constructing a promise, for which the procedure is defined in 27.2.3 The Promise Constructor.

    Note that at this point the await operator is not yet relevant. This constructor callback executes synchronously as the promise is created. Only after the promise callback has executed, the new Promise() expression has fully evaluated, and the return statement can execute. Only then the await operator will execute.

    Then the second question in the original post:

    Are they being passed implicitly by the await clause?

    The procedure for the await operator can be found at 27.7.5.3 Await (value) in the same specification.

    It gets the promise that its operand evaluates to. NB: if it was not a promise, a new promise is created for it. But this is not your case.

    Then it creates(!) both the onFulfilled and onRejected handlers (functions), which are passed as callbacks to the promise’s then method. Among other things, these handlers take care of resuming a suspended function.

    Finally, the await makes the function return, and it returns a pending promise (a different one from the one you created).

    The await operator does not call the resolve or reject functions that you received in the promise constructor callback. The await operator merely attaches then callbacks to the promise: one for when it fulfills, another for when it rejects. These handlers are not resolve and reject. While the resolve and reject functions set the state of a promise, the handlers (that await registers) merely react to a state change, just like you expect from any then/catch callback.

    Login or Signup to reply.
  4. If I understand your question correctly, you are asking where resolve and reject comes from?

    The answer is the person who wrote the code for the Promise object. That’s how promises are designed to work. It has nothing to do with await. It has nothing to do with javascript itself (except for the fact that the ECMAScript standard specifies how Promises are supposed to work).

    Let me give you an example.

    I, slebetman, give you a library that returns some number;

    The API for that library is:

    makeNumberGetter((getter) => {
        const n = getter();
    
        console.log(n);
    });
    

    Where does the getter function come form? It comes form me, slebetman, because I wrote the function like this:

    function makeNumberGetter (userFunction) {
        const theAnswerToLife_TheUniverse_AndEverything = 42;
    
        function theGetterFunctionIAmGoingToGiveYou () {
            return theAnswerToLife_TheUniverse_AndEverything;
        }
    
        // HERE is where I give you the `getter()` function:
        userFunction(theGetterFunctionIAmGoingToGiveYou);
    }
    

    In the Promise object there is a similar construct (but probably written in C++ instead of plain JS).

    A simple poor-man’s Promise constructor (that doesn’t comply with the specification) can be written like this:

    // This cannot be chained.
    // That means x.then().then() won't work:
    function slebetmansPromise (fn) {
        let thenCallback;
        let catchCallback;
    
        function resolve (x) {
            if (thenCallback) thenCallback(x);
        }
    
        function reject (x) {
            if (catchCallback) catchCallback(x);
        }
    
        function then (x) {
            thenCallback = x;
            return ret;
        }
    
        function catch (x) {
            catchCallback = x;
            return ret;
        }
    
        const ret = {
            then,
            catch,        
        }
    
        // HERE is where I'm giving you resolve and reject
        fn(resolve, reject);
    
        return ret;
    }
    

    Now you can use it like:

    slebetmansPromise((resolve, reject) => {
        setTimeout(() => resolve('OK'), 1000);
    })
    .then((x) => {
        console.log(x); // logs OK
    });
    

    Technically even this should work;

    async function main () {
        const result = await slebetmansPromise((resolve, reject) => {
            setTimeout(() => resolve('OK'), 1000);
        });
    
        console.log(result); // should log OK
    }
    
    main();
    

    That’s because await works with any function that returns an object that has a .then() function.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search