承诺回调承诺的回调

kst*_*tis 55 javascript node.js promise es6-promise

关于这两个很重要的来源:NZakas - 承诺链中的承诺MDN承诺,我想问以下问题:

每次我们从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
});
Run Code Online (Sandbox Code Playgroud)

在这个例子中,p2是一个承诺.p3也是源于p1履行处理者的承诺.但是p2 !== p3.相反,p2不知何故神奇地解决43(如何?),然后将该值传递给p3履行处理程序.即使是这里的句子也令人困惑.

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

Dan*_*mov 39

假设抛出内部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
Run Code Online (Sandbox Code Playgroud)

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

return checkCache().then(cachedValue => {
  if (cachedValue) {
    return cachedValue
  }

  // I want to do some async work here
})
Run Code Online (Sandbox Code Playgroud)

但是,如果我在那里进行异步工作,那就太晚了,return或者throw不是吗?

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
  })
})
Run Code Online (Sandbox Code Playgroud)

这就是为什么如果你无法解决另一个Promise, Promises就没用了.

这并不意味着你的例子p2成为 p3.它们是单独的Promise对象.然而,通过p2then()那个产品返回,p3你说"我想要p3解决任何结果p2,无论是成功还是失败".

至于如何发生这种情况,它是特定于实现的.在内部,你可以想到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
  })
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 值得注意的是,传递给 then 的每个函数都应该具有类型 a -> Promise b ,即返回另一个 Promise 。然而,我们决定只要传递普通函数“a -> b”,“then”就会隐式进行包装。 (2认同)

Vin*_*ing 19

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

无论何时使用关键字,return您都将结果作为参数传递给next then的回调.

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

你的代码:

p3.then(function(value) {
    // second fulfillment handler
    console.log(value);     // 43
});
Run Code Online (Sandbox Code Playgroud)

等于:

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'
});
Run Code Online (Sandbox Code Playgroud)

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

p1.then(resultOfP1 => p2) // the `return` is implied since it's a one-liner
.then(resultOfP2 => console.log(resultOfP2)); 
Run Code Online (Sandbox Code Playgroud)


Tho*_*mas 5

在这个例子中,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
    }
}
Run Code Online (Sandbox Code Playgroud)

整个事情变得相当复杂,因为你必须小心,承诺是否已经解决,还有一些事情。


小智 5

我将尝试更规范地回答“为什么then回调可以Promise自行返回”这个问题。为了换个角度,我将Promises 与不太复杂和容易混淆的容器类型 - Arrays 进行比较。

APromise是未来值的容器。AnArray是任意数量值的容器。

我们无法将普通函数应用于容器类型:

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

sqr(xs); // fails
sqr(p); // fails
Run Code Online (Sandbox Code Playgroud)

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

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

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

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}
Run Code Online (Sandbox Code Playgroud)

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))
Run Code Online (Sandbox Code Playgroud)

flatten只需接受一个函数和一个值,将函数应用于该值并解开结果,从而将嵌套数组结构减少一级。

简单来说,then在s的上下文中Promise相当于在s的上下文中map组合。这种行为极其重要。我们不仅可以将普通函数应用于 a ,还可以将函数本身返回 a flattenArrayPromisePromise

事实上,这是函数式编程的领域。A是monadPromise的具体实现,是/ ,返回 a 的函数是 Monadic 函数。当你了解API 时,你基本上就了解了所有 monad。thenbindchainPromisePromise