Promise callbacks returning promises

后端 未结 4 801
谎友^
谎友^ 2020-12-12 16:30

With regard to these great two sources: NZakas - Returning Promises in Promise Chains and MDN Promises, I would like to ask the following:

Each time that we return a

4条回答
  •  无人及你
    2020-12-12 16:55

    Let’s say that throwing inside then() callback rejects the result promise with a failure, and returning from then() callback fulfills the result promise with a success value.

    let p2 = p1.then(() => {
      throw new Error('lol')
    })
    // p2 was rejected with Error('lol')
    
    let p3 = p1.then(() => {
      return 42
    })
    // p3 was fulfilled with 42
    

    But sometimes, even inside the continuation, we don’t know whether we have succeeded or not. We need more time.

    return checkCache().then(cachedValue => {
      if (cachedValue) {
        return cachedValue
      }
    
      // I want to do some async work here
    })
    

    However, if I do async work there, it would be too late to return or throw, wouldn’t it?

    return checkCache().then(cachedValue => {
      if (cachedValue) {
        return cachedValue
      }
    
      fetchData().then(fetchedValue => {
        // Doesn’t make sense: it’s too late to return from outer function by now.
        // What do we do?
    
        // return fetchedValue
      })
    })
    

    This is why Promises wouldn’t be useful if you couldn’t resolve to another Promise.

    It doesn’t mean that in your example p2 would become p3. They are separate Promise objects. However, by returning p2 from then() that produces p3 you are saying “I want p3 to resolve to whatever p2 resolves, whether it succeeds or fails”.

    As for how this happens, it’s implementation-specific. Internally you can think of then() as creating a new Promise. The implementation will be able to fulfill or reject it whenever it likes. Normally, it will automatically fulfill or reject it when you return:

    // Warning: this is just an illustration
    // and not a real implementation code.
    // For example, it completely ignores
    // the second then() argument for clarity,
    // and completely ignores the Promises/A+
    // requirement that continuations are
    // run asynchronously.
    
    then(callback) {
      // Save these so we can manipulate
      // the returned Promise when we are ready
      let resolve, reject
    
      // Imagine this._onFulfilled is an internal
      // queue of code to run after current Promise resolves.
      this._onFulfilled.push(() => {
        let result, error, succeeded
        try {
          // Call your callback!
          result = callback(this._result)
          succeeded = true
        } catch (err) {
          error = err
          succeeded = false
        }
    
        if (succeeded) {
          // If your callback returned a value,
          // fulfill the returned Promise to it
          resolve(result)
        } else {
          // If your callback threw an error,
          // reject the returned Promise with it
          reject(error)
        }
      })
    
      // then() returns a Promise
      return new Promise((_resolve, _reject) => {
        resolve = _resolve
        reject = _reject
      })
    }
    

    Again, this is very much pseudo-code but shows the idea behind how then() might be implemented in Promise implementations.

    If we want to add support for resolving to a Promise, we just need to modify the code to have a special branch if the callback you pass to then() returned a Promise:

        if (succeeded) {
          // If your callback returned a value,
          // resolve the returned Promise to it...
          if (typeof result.then === 'function') {
            // ...unless it is a Promise itself,
            // in which case we just pass our internal
            // resolve and reject to then() of that Promise
            result.then(resolve, reject)
          } else {
            resolve(result)
          }
        } else {
          // If your callback threw an error,
          // reject the returned Promise with it
          reject(error)
        }
      })
    

    Let me clarify again that this is not an actual Promise implementation and has big holes and incompatibilities. However it should give you an intuitive idea of how Promise libraries implement resolving to a Promise. After you are comfortable with the idea, I would recommend you to take a look at how actual Promise implementations handle this.

提交回复
热议问题