承诺回调承诺的回调

时间:2016-03-02 13:00:09

标签: javascript node.js promise es6-promise

关于这两个很重要的来源: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 个答案:

答案 0 :(得分:36)

假设抛出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
  })
})

如果你不能解析另一个Promise ,这就是Promises没用的原因。

这并不意味着您的示例p2 p3。它们是单独的Promise对象。但是,通过返回p2生成then()的{​​{1}},您会说“我希望p3解析为p3解析,是否成功或者失败“。

至于 这是怎么发生的,它是特定于实现的。在内部,您可以将p2视为创建新的Promise。实施将能够随时满足或拒绝它。通常,当您返回时,它会自动履行或拒绝它:

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()的{​​{1}}返回Promise时有一个特殊的分支:

callback

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

答案 1 :(得分:17)

基本上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)); 

答案 2 :(得分:3)

  
    

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

  

简化版本如何工作(仅伪代码)

require_once("simpledom.php");

$url="http://google.com";
$sPage = file_get_contents($url);
$sPageContent = new simple_html_dom();
$sPageContent->load($sPage);
$sObjects = $sPageContent->find('unknown');

foreach($sObjects as $sKey)
{
    var_dump($sKey->_[4]); /*this var_dump shows the stuff */
    $showres = $sKey->_[4]
}

/* this variable should hold the string but it shows nothing */
echo $showres;
整个事情变得更加复杂,因为你必须要小心,承诺已经解决了,还有更多的东西。

答案 3 :(得分:2)

我会尝试回答问题"为什么then回调可以返回Promise自己" 更具规范性。为了采取不同的角度,我将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只接受一个函数和一个值,将函数应用于该值并展开结果,从而将嵌套数组结构减少一级。

简单地说,在then s的上下文中,Promise相当于mapflatten s的上下文中与Array相结合。这种行为非常重要。 我们不仅可以将正常函数应用于Promise,还可以将函数本身返回Promise

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