Search code examples
javascriptasync-awaitpromise

What functions are passed to the Promise when awaiting in javascript?


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?

Solution

  • 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.