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
Promise callbacks returning promises
提问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 Chains和MDN 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, 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
的履行处理程序。甚至这里的句子也令人困惑。
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 return
or throw
, wouldn't it?
但是,如果我在那里进行异步工作,那么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
})
})
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 p2
would becomep3
. They are separate Promise objects. 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”.
这并不意味着在你的例子中p2
会变成p3
. 它们是单独的 Promise 对象。然而,通过p2
从then()
那个产品返回,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 callback
you 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 p3
is return
-ing an another promise : 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
.
基本上p3
是 return
-ing 另一个承诺:p2
。这意味着 的结果p2
将作为参数传递给下一个then
回调,在这种情况下它解析为43
。
Whenever you are using the keyword return
you 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 then
callbacks can return Promise
s themselves"more canonical. To take a different angle, I compare Promise
s with a less complex and confusing container type - Array
s.
我将尝试更规范地回答“为什么then
回调可以Promise
自己返回s”的问题。换个角度来看,我将Promise
s 与不那么复杂和容易混淆的容器类型 - Array
s 进行比较。
A Promise
is a container for a future value.
An Array
is 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}
sqra
acts 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 sqrp
be 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 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. This behavior is extremely important. We can apply not only normal functions to a Promise
but also functions that itself return a Promise
.
简单的说,then
在Promise
s的上下文中相当于在s的上下文中map
结合。这种行为极其重要。我们不仅可以对 a 应用普通函数,还可以对本身返回 a 的函数应用。flatten
Array
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. When you understand the Promise
API you basically understand all monads.
事实上,这是函数式编程的领域。APromise
是monad的特定实现,then
是bind
/chain
并且返回 a 的函数Promise
是 monadic 函数。当您了解Promise
API 时,您基本上了解所有 monad。