首页 文章

承诺回调承诺的回调

提问于
浏览
48

关于这两个很大的来源:NZakas - Returning Promises in Promise ChainsMDN Promises,我想问一下:

每次我们从promise履行处理程序返回一个值时,该值是如何传递给从同一个处理程序返回的新promise?

例如,

let p1 = new Promise(function(resolve, reject) {
    resolve(42);
});

let p2 = new Promise(function(resolve, reject) {
    resolve(43);
});

let p3 = p1.then(function(value) {
    // first fulfillment handler
    console.log(value);     // 42
    return p2;
});

p3.then(function(value) {
    // second fulfillment handler
    console.log(value);     // 43
});

在此示例中, p2 是一个承诺 . p3 也是来自 p1 履行处理程序的承诺 . 但是 p2 !== p3 . 相反 p2 以某种方式神奇地解析为 43 (如何?)然后将该值传递给 p3 的履行处理程序 . 即使是这里的句子也令人困惑 .

你能否向我解释一下究竟发生了什么?我对这个概念感到困惑 .

4 回答

  • 34

    假设抛出 then() 回调函数拒绝结果承诺失败,并且从 then() 回调返回以成功值实现结果承诺 .

    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
    

    但有时,即使在延续期内,我们也不知道我们是否成功 . 我们需要更多时间 .

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

    但是,如果我在那里进行异步工作,那么 returnthrow 就太晚了,不是吗?

    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
      })
    })
    

    这就是为什么如果你不能 resolve to another Promise ,Promises就没用了 .

    这并不意味着在你的例子中 p2 会变成 p3 . 它们是单独的Promise对象 . 但是,通过从产生 p3then() 返回 p2 ,您说 “I want p3 to resolve to whatever p2 resolves, whether it succeeds or fails”.

    至于如何发生这种情况,它是特定于实现的 . 在内部,您可以将 then() 视为创建新的Promise . 实施将能够随时满足或拒绝它 . 通常情况下,当您返回时,它会自动完成或拒绝它:

    // 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
      })
    }
    

    同样,这是非常伪代码,但显示了如何在Promise实现中实现 then() 的想法 .

    如果我们想要添加对Promise的解析支持,我们只需要修改代码以便在传递给 then()callback 返回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)
        }
      })
    

    让我再次澄清,这不是一个真正的Promise实现,并且有很大的漏洞和不兼容性 . 但是,它应该让您直观地了解Promise库如何实现解析Promise . 在您对这个想法感到满意之后,我建议您先看看实际的Promise实现方式handle this .

  • 15

    基本上 p3return - 另一个承诺: p2 . 这意味着 p2 的结果将作为参数传递给下一个 then 回调,在这种情况下它会解析为 43 .

    每当您使用关键字 return 时,您都会将结果作为参数传递给下一个 then 的回调 .

    let p3 = p1.then(function(value) {
        // first fulfillment handler
        console.log(value);     // 42
        return p2;
    });
    

    你的代码:

    p3.then(function(value) {
        // second fulfillment handler
        console.log(value);     // 43
    });
    

    等于:

    p1.then(function(resultOfP1) {
        // resultOfP1 === 42
        return p2; // // Returning a promise ( that might resolve to 43 or fail )
    })
    .then(function(resultOfP2) {
        console.log(resultOfP2) // '43'
    });
    

    顺便说一下,我注意到你使用的是ES6语法,你可以通过使用胖箭头语法来获得更轻的语法:

    p1.then(resultOfP1 => p2) // the `return` is implied since it's a one-liner
    .then(resultOfP2 => console.log(resultOfP2));
    
  • 3

    在这个例子中,p2是一个承诺 . p3也是源自p1履行处理程序的承诺 . 但是p2!== p3 . 相反,p2以某种方式神奇地解析为43(如何?),然后将该值传递给p3的履行处理程序 . 即使是这里的句子也令人困惑 .

    一个简化版本如何工作(只有伪代码)

    function resolve(value){
        if(isPromise(value)){
            value.then(resolve, reject);
        }else{
            //dispatch the value to the listener
        }
    }
    

    整个事情要复杂得多,因为你必须要小心,承诺已经解决了,还有更多的东西 .

  • 1

    我将尝试回答“为什么 then 回调可以返回 Promise s”这个问题更具规范性 . 为了采取不同的角度,我将 Promise 与较不复杂和容易混淆的容器类型进行比较 - Array s .

    Promise 是未来值的容器 . Array 是任意数量值的容器 .

    我们不能将常规函数应用于容器类型:

    const sqr = x => x * x;
    const xs = [1,2,3];
    const p = Promise.resolve(3);
    
    sqr(xs); // fails
    sqr(p); // fails
    

    我们需要一种机制将它们提升到特定容器的上下文中:

    xs.map(sqr); // [1,4,9]
    p.then(sqr); // Promise {[[PromiseValue]]: 9}
    

    但是当提供的函数本身返回相同类型的容器时会发生什么?

    const sqra = x => [x * x];
    const sqrp = x => Promise.resolve(x * x);
    const xs = [1,2,3];
    const p = Promise.resolve(3);
    
    xs.map(sqra); // [[1],[4],[9]]
    p.then(sqrp); // Promise {[[PromiseValue]]: 9}
    

    sqra 表现得像预期的那样 . 它只返回一个具有正确值的嵌套容器 . 这显然不是很有用 .

    但是如何解释 sqrp 的结果呢?如果我们遵循自己的逻辑,它必须像 Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}} - 但事实并非如此 . 那么这里有什么魔力呢?

    为了重建机制,我们只需要稍微调整一下我们的 map 方法:

    const flatten = f => x => f(x)[0];
    const sqra = x => [x * x];
    const sqrp = x => Promise.resolve(x * x);
    const xs = [1,2,3];
    
    xs.map(flatten(sqra))
    

    flatten 只需要一个函数和一个值,适用函数的值和解包结果,因此它将嵌套的数组结构减少一个级别 .

    简单地说, thenPromise 的上下文中等同于 mapArray 的上下文中的 flatten 相结合 . 这种行为非常重要 . We can apply not only normal functions to a Promise but also functions that itself return a Promise.

    实际上,这是函数式编程的领域 . Promisemonad 的具体实现, thenbind / chain ,返回 Promise 的函数是monadic函数 . 当您了解 Promise API时,您基本上了解所有monad .

相关问题