BFE.dev solution for JavaScript Quiz
2. Promise executor

TL;DR

Once Promise is fulfilled or rejected, it cannot be fulfilled or rejected again.

So for the code in the question:

new Promise((resolve, reject) => {
  resolve(1)
  resolve(2)
  reject('error')
}).then(
  (value) => {
    console.log(value)
  },
  (error) => {
    console.log('error')
  }
)

It is the same as below:

new Promise((resolve, reject) => {
  resolve(1)
}).then(
  (value) => {
    console.log(value)
  },
  (error) => {
    console.log('error')
  }
)

So 1 is the result.

Below is detailed explanation based on ECMAScript spec

Promise(executor)

According to ECMAScript spec, what happens in new Promise(...) is defined as:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. If IsCallable(executor) is false, throw a TypeError exception.
  3. Let promise be ? OrdinaryCreateFromConstructor(NewTarget, "%Promise.prototype%", « [[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]], [[PromiseIsHandled]] »).
  4. Set promise.[[PromiseState]] to pending.
  5. Set promise.[[PromiseFulfillReactions]] to a new empty List.
  6. Set promise.[[PromiseRejectReactions]] to a new empty List.
  7. Set promise.[[PromiseIsHandled]] to false.
  8. Let resolvingFunctions be CreateResolvingFunctions(promise).
  9. Let completion be Completion(Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)).
  10. If completion is an abrupt completion, then
    1. a. Perform ? Call(resolvingFunctions.[[Reject]], undefined, « completion.[[Value]] »).
  11. Return promise.

We can see that

  1. it creates an empty promise object
  2. creates the resolve() and reject() by CreateResolvingFunctions
  3. the executor is called with above 2 functions passed.

Properties of Promise Instances

According to ECMAScript spec, a promise have following internal data slot.

Internal Slot Type Description
[[PromiseState]] pending, fulfilled, or rejected Governs how a promise will react to incoming calls to its then method.
[[PromiseResult]] an ECMAScript language value The value with which the promise has been fulfilled or rejected, if any. Only meaningful if [[PromiseState]] is not pending.
[[PromiseFulfillReactions]] a List of PromiseReaction Records Records to be processed when/if the promise transitions from the pending state to the fulfilled state.
[[PromiseRejectReactions]] a List of PromiseReaction Records Records to be processed when/if the promise transitions from the pending state to the rejected state.
[[PromiseIsHandled]] a Boolean Indicates whether the promise has ever had a fulfillment or rejection handler; used in unhandled rejection tracking.

[[PromiseState]] is very important, it is internal state that we often refer to whether a Promise is rejected or fulfilled.

[[PromiseResult]] is the final value that it settles to

[[PromiseFulfillReactions]] and [[PromiseRejectReactions]] are the callbacks

[[PromiseIsHandled]] is why we often see unhandle rejection error.

CreateResolvingFunctions ( promise )

According to ECMAScript spec, resolve() and reject() are created as below

  1. Let alreadyResolved be the Record { [[Value]]: false }.
  2. Let stepsResolve be the algorithm steps defined in Promise Resolve Functions.
  3. Let lengthResolve be the number of non-optional parameters of the function definition in Promise Resolve Functions.
  4. Let resolve be CreateBuiltinFunction(stepsResolve, lengthResolve, "", « [[Promise]], [[AlreadyResolved]] »).
  5. Set resolve.[[Promise]] to promise.
  6. Set resolve.[[AlreadyResolved]] to alreadyResolved.
  7. Let stepsReject be the algorithm steps defined in Promise Reject Functions.
  8. Let lengthReject be the number of non-optional parameters of the function definition in Promise Reject Functions.
  9. Let reject be CreateBuiltinFunction(stepsReject, lengthReject, "", « [[Promise]], [[AlreadyResolved]] »).
  10. Set reject.[[Promise]] to promise.
  11. Set reject.[[AlreadyResolved]] to alreadyResolved.
  12. Return the Record { [[Resolve]]: resolve, [[Reject]]: reject }.

We can see that

  1. both resolve() and reject() holds internal reference to the Promise by [[Promise]]
  2. both resolve() and reject() has a property [[AlreadyResolved]], which points to a single state alreadyResolved, meaning resolve() and reject() are checking the same value

resolve()

According to ECMAScript spec,

  1. Let F be the active function object.
  2. Assert: F has a [[Promise]] internal slot whose value is an Object.
  3. Let promise be F.[[Promise]].
  4. Let alreadyResolved be F.[[AlreadyResolved]].
  5. If alreadyResolved.[[Value]] is true, return undefined.
  6. Set alreadyResolved.[[Value]] to true.
  7. If SameValue(resolution, promise) is true, then
    1. Let selfResolutionError be a newly created TypeError object.
    2. Perform RejectPromise(promise, selfResolutionError).
    3. Return undefined.
  8. If resolution is not an Object, then
    1. Perform FulfillPromise(promise, resolution).
    2. Return undefined.
  9. Let then be Completion(Get(resolution, "then")).
  10. If then is an abrupt completion, then
    1. Perform RejectPromise(promise, then.[[Value]]).
    2. Return undefined.
  11. Let thenAction be then.[[Value]].
  12. If IsCallable(thenAction) is false, then
    1. Perform FulfillPromise(promise, resolution).
    2. Return undefined.
  13. Let thenJobCallback be HostMakeJobCallback(thenAction).
  14. Let job be NewPromiseResolveThenableJob(promise, resolution,thenJobCallback).
  15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
  16. Return undefined.

There are quite some steps

  1. it checks if promise is already resolved, return if so
  2. it forbids resolve() to be called with promise itself, it creates infinite chaining
  3. resolves to the value if the argument is not Object
  4. if the argument is thenable, chain them

The step 5 clearly tells us that resolve() does nothing if promise is already resolved.

reject()

According to ECMASCript spec, reject() internals are defined as:

  1. Let F be the active function object.
  2. Assert: F has a [[Promise]] internal slot whose value is an Object.
  3. Let promise be F.[[Promise]].
  4. Let alreadyResolved be F.[[AlreadyResolved]].
  5. If alreadyResolved.[[Value]] is true, return undefined.
  6. Set alreadyResolved.[[Value]] to true.
  7. Perform RejectPromise(promise, reason).
  8. Return undefined.

The step 5 clearly tells us that reject() does nothing if promise is already resolved.

You might also be able to find a solution fromcommunity posts or fromAI solution.