Javascript 承诺回调返回承诺

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35747957/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:10:18  来源:igfitidea点击:

Promise callbacks returning promises

javascriptnode.jspromisees6-promise

提问by kstratis

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

关于这两个重要的来源:NZakas - Returning Promises in Promise ChainsMDN Promises,我想问以下问题:

Each time that we return a value from a promise fulfillment handler, how is that value passed to the new promise returned from that same handler?

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

For instance,

例如,

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

In this example, p2is a promise. p3is also a promise originating from p1's fulfillment handler. However p2 !== p3. Instead p2somehow magically resolves to 43(how?) and that value is then passed to p3's fulfillment handler. Even the sentence here is confusing.

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

Could you please explain to me what exactly is going on here? I am totally confused over this concept.

你能向我解释一下这里到底发生了什么吗?我完全对这个概念感到困惑。

采纳答案by Dan Abramov

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.

假设抛出内部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

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 returnor throw, wouldn't it?

但是,如果我在那里进行异步工作,那么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
  })
})

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

这就是为什么如果您无法解析另一个 Promise 则 Promise将没有用的原因。

It doesn't mean that in your example p2would becomep3. They are separate Promise objects. However, by returning p2from then()that produces p3you are saying “I want p3to resolve to whatever p2resolves, whether it succeeds or fails”.

这并不意味着在你的例子中p2变成p3. 它们是单独的 Promise 对象。然而,通过p2then()那个产品返回,p3你是在说“我想p3解决任何p2解决的问题,无论它是成功还是失败”。

As for howthis 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:

至于这是如何发生的,它是特定于实现的。在内部,您可以将其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
  })
}

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

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

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 callbackyou pass to then()returned a 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)
    }
  })

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.

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

回答by Vincent Taing

Basically p3is return-ing an another promise : p2. Which means the result of p2will be passed as a parameter to the next thencallback, in this case it resolves to 43.

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

Whenever you are using the keyword returnyou are passing the result as a parameter to next then's callback.

每当您使用关键字时,return您都将结果作为参数传递给 nextthen的回调。

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

Your code :

你的代码:

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

Is equal to:

等于:

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

Btw, I've noticed that you are using ES6 syntax, you can have a lighter syntax by using fat arrow syntax :

顺便说一句,我注意到您使用的是 ES6 语法,您可以使用粗箭头语法来简化语法:

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

回答by Thomas

In this example, p2 is a promise. p3 is also a promise originating from p1's fulfillment handler. However p2 !== p3. Instead p2 somehow magically resolves to 43 (how?) and that value is then passed to p3's fulfillment handler. Even the sentence here is confusing.

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

a simplified version how this works (only pseudocode)

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

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

the whole thing is quite more complicated since you have to take care, wether the promise has already been resolved and a few more things.

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

回答by Thomas

I'll try to answer the question "why thencallbacks can return Promises themselves"more canonical. To take a different angle, I compare Promises with a less complex and confusing container type - Arrays.

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

A Promiseis a container for a future value. An Arrayis a container for an arbitrary number of values.

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

We can't apply normal functions to container types:

我们不能将普通函数应用于容器类型:

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

sqr(xs); // fails
sqr(p); // fails

We need a mechanism to lift them into the context of a specific container:

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

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

But what happens when the provided function itself returns a container of the same type?

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

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}

sqraacts like expected. It just returns a nested container with the correct values. This is obviously not very useful though.

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

But how can the result of sqrpbe interpreted? If we follow our own logic, it had to be something like Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}}- but it is not. So what magic is going on here?

但是如何sqrp解释结果呢?如果我们遵循我们自己的逻辑,它必须是这样的Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}}——但事实并非如此。那么这里发生了什么魔法呢?

To reconstruct the mechanism we merely need to adapt our mapmethod a bit:

为了重建机制,我们只需要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))

flattenjust takes a function and a value, applies the function to the value and unwraps the result, thus it reduces a nested array structure by one level.

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

Simply put, thenin the context of Promises is equivalent to mapcombined with flattenin the context of Arrays. This behavior is extremely important. We can apply not only normal functions to a Promisebut also functions that itself return a Promise.

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

In fact this is the domain of functional programming. A Promiseis a specific implementation of a monad, thenis bind/chainand a function that returns a Promiseis a monadic function. When you understand the PromiseAPI you basically understand all monads.

事实上,这是函数式编程的领域。APromisemonad的特定实现,thenbind/chain并且返回 a 的函数Promise是 monadic 函数。当您了解PromiseAPI 时,您基本上了解所有 monad。