简体   繁体   English

承诺回调承诺的回调

[英]Promise callbacks returning promises

With regard to these great two sources: NZakas - Returning Promises in Promise Chains and MDN Promises , I would like to ask the following: 关于这两个很重要的来源: NZakas - 承诺链中的承诺MDN承诺 ,我想问以下问题:

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? 每次我们从promise履行处理程序返回一个值时,该值是如何传递给从同一个处理程序返回的新promise?

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, p2 is a promise. 在这个例子中, p2是一个承诺。 p3 is also a promise originating from p1 's fulfillment handler. p3也是源自p1履行处理程序的承诺。 However p2 !== p3 . 但是p2 !== p3 Instead p2 somehow magically resolves to 43 (how?) and that value is then passed to p3 's fulfillment handler. 相反, p2以某种方式神奇地解析为43 (如何?),然后将该值传递给p3的履行处理程序。 Even the sentence here is confusing. 即使是这里的句子也令人困惑。

Could you please explain to me what exactly is going on here? 你能否向我解释一下究竟发生了什么? I am totally confused over this concept. 我对这个概念感到困惑。

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()回调中抛出会因失败而拒绝结果promise,而从then()回调返回会以成功值实现结果promise。

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? 但是,如果我在那里进行异步工作,那么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, Promises就没用了。

It doesn't mean that in your example p2 would become p3 . 这并不意味着在你的例子中p2变成 p3 They are separate Promise objects. 它们是单独的Promise对象。 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”. 但是,通过从生成p3 then()返回p2 ,你会说“我希望p3解析为任何p2解析,无论是成功还是失败”。

As for how this happens, it's implementation-specific. 至于如何发生这种情况,它是特定于实现的。 Internally you can think of then() as creating a new Promise. 在内部,您可以将then()视为创建新的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. 同样,这是非常多的伪代码,但展示了如何在Promise实现中实现then()的想法。

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: 如果我们想要添加对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)
    }
  })

Let me clarify again that this is not an actual Promise implementation and has big holes and incompatibilities. 让我再次澄清,这不是一个真正的Promise实现,并且有很大的漏洞和不兼容性。 However it should give you an intuitive idea of how Promise libraries implement resolving to a Promise. 但是,它应该让您直观地了解Promise库如何实现解析Promise。 After you are comfortable with the idea, I would recommend you to take a look at how actual Promise implementations handle this . 在您对这个想法感到满意之后,我建议您先看看实际的Promise实现如何处理这个问题

Basically p3 is return -ing an another promise : p2 . 基本上p3正在return另一个承诺: p2 Which means the result of p2 will be passed as a parameter to the next then callback, in this case it resolves to 43 . 这意味着结果p2将作为一个参数到下一个要传递then回调,在这种情况下,解析为43

Whenever you are using the keyword return you are passing the result as a parameter to next then 's callback. 当你正在使用的关键字return您传递的结果作为参数传递给下一then的回调。

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

In this example, p2 is a promise. 在这个例子中,p2是一个承诺。 p3 is also a promise originating from p1's fulfillment handler. p3也是源自p1履行处理程序的承诺。 However p2 !== p3. 但是p2!== p3。 Instead p2 somehow magically resolves to 43 (how?) and that value is then passed to p3's fulfillment handler. 相反,p2以某种方式神奇地解析为43(如何?),然后将该值传递给p3的履行处理程序。 Even the sentence here is confusing. 即使是这里的句子也令人困惑。

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. 整个事情要复杂得多,因为你必须要小心,承诺已经解决了,还有更多的东西。

I'll try to answer the question "why then callbacks can return Promise s themselves" more canonical. 我将尝试回答“为什么then回调可以让Promise自己回归”更为规范的问题。 To take a different angle, I compare Promise s with a less complex and confusing container type - Array s. 为了采取不同的角度,我将Promise s与一个不太复杂和容易混淆的容器类型 - Array s进行比较。

A Promise is a container for a future value. Promise是未来价值的容器。 An Array is a container for an arbitrary number of values. Array是任意数量值的容器。

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}

sqra acts like expected. sqra表现得像预期的那样。 It just returns a nested container with the correct values. 它只返回一个具有正确值的嵌套容器。 This is obviously not very useful though. 这显然不是很有用。

But how can the result of sqrp be interpreted? 但是如何解释sqrp的结果呢? If we follow our own logic, it had to be something like Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}} - but it is not. 如果我们遵循自己的逻辑,那就必须像Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}} - 但事实并非如此。 So what magic is going on here? 那么这里有什么魔力呢?

To reconstruct the mechanism we merely need to adapt our map method 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))

flatten just 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, then in the context of Promise s is equivalent to map combined with flatten in the context of Array s. 简单地说, thenPromise的上下文中,相当于在Array的上下文中与flatten结合的map This behavior is extremely important. 这种行为非常重要。 We can apply not only normal functions to a Promise but also functions that itself return a Promise . 我们不仅可以将正常函数应用于Promise ,还可以将函数本身应用于Promise

In fact this is the domain of functional programming. 实际上,这是函数式编程的领域。 A Promise is a specific implementation of a monad , then is bind / chain and a function that returns a Promise is a monadic function. Promisemonad的特定实现, thenbind / chain ,返回Promise的函数是monadic函数。 When you understand the Promise API you basically understand all monads. 当您了解Promise API时,您基本上了解所有monad。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Javascript然后通过回调返回Promise - Javascript returning promises with then callbacks 为 Promise 绑定多个回调返回第一个 Promise 的解析值,而不是第二个 - Binding multiple callbacks for promises returns resolve value of first promise, not second 在服务中使用promises但不返回承诺 - Use promises within service but NOT returning a promise 返回一个新的 promise 并加强了备份承诺 - Returning a new promise reinforced with backup promises Promise.all()基于返回Promises的函数 - Promise.all() based on functions returning Promises 使用promise或callback时,装饰器函数返回未定义的 - Decorator function returning undefined when using promises or callbacks 如何支持API调用以将Promise或Callbacks与Standard Promise,无延迟或外部Promise库一起使用? - How to support API calls to use a Promise or Callbacks with Standard Promises, no defers or external Promise Library? Promise.all为数组的promise返回promise,但为单个promise返回正确的值 - Promise.all returning promise for array of promises, but returning correct value for single promise 返回的Promise.promise.promise数组使用过滤器不起作用 - Returning an array of promises for Promise.all using filter not working NodeJs forEach request-promise 在返回之前等待所有承诺 - NodeJs forEach request-promise wait for all promises before returning
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM