我有一个 es6 类,有一个 init()
方法负责获取数据,转换它,然后用新转换的数据更新类的属性 this.data
。到目前为止,一切都很好。该类本身有另一个 getPostById()
方法,只是做它听起来的样子。这是该类的代码:
class Posts {
constructor(url) {
this.ready = false
this.data = {}
this.url = url
}
async init() {
try {
let res = await fetch( this.url )
if (res.ok) {
let data = await res.json()
// Do bunch of transformation stuff here
this.data = data
this.ready = true
return data
}
}
catch (e) {
console.log(e)
}
}
getPostById(id){
return this.data.find( p => p.id === id )
}
}
直截了当,除了我在 --- init()
async/await
机制。现在,此代码将正常工作:
let allPosts = new Posts('https://jsonplaceholder.typicode.com/posts')
allPosts.init()
.then( d => console.log(allPosts.getPostById(4)) )
// resulting Object correctly logged in console
但它只会打印到控制台中:How could I use allPosts.getPostById(4)
as a return
of a function?
喜欢:
let myFunc = async () => {
const postId = 4
await allPosts.init() // I need to wait for this to finish before returning
// This is logging correct value
console.log( 'logging: ' + JSON.stringify(allPosts.getPostById( postId ), null, 4) )
// How can I return the RESULT of allPosts.getPostById( postId ) ???
return allPosts.getPostById( postId )
}
myFunc()
返回一个 Promise
但不是最终值。我已经阅读了几篇关于该主题的相关文章,但它们都给出了日志记录的示例,永不返回。
这是一个小提琴,包括两种处理方式 init()
:使用 Promise
和使用 async/await
。无论我尝试什么,我都无法使用 getPostById(id)
的最终值。
这篇文章的问题是: 如何创建一个函数来返回 getPostById(id)
的值?
编辑:
很多很好的答案试图解释什么是关于主执行循环的承诺。在看了很多视频和其他好的读物之后,这是我现在的理解:
我的函数 init()
正确返回。然而,在主事件循环中:它返回 一个 Promise ,然后我的工作是从一个 并行 循环(不是一个新的真实线程)中捕获这个 Promise 的结果。为了从并行循环中捕获结果,有两种方法:
使用
.then( value => doSomethingWithMy(value) )
使用
let value = await myAsyncFn()
。现在这是愚蠢的打嗝:
await 只能在
async
函数中使用:p
因此它自己返回一个 Promise,可与 await
一起使用,它应该嵌入到 async
函数中,该函数将与 await
c2eb…
这意味着我们不能真正等待 Promise:相反,我们应该无限期地捕获并行循环:使用 .then()
或 async/await
。
谢谢您的帮助 !
原文由 Jona Rodrigues 发布,翻译遵循 CC BY-SA 4.0 许可协议
至于你的评论;我会将其添加为答案。
你用 JavaScript 编写的代码在一个线程上运行,这意味着如果你的代码实际上可以等待某些东西,它将阻止你的任何其他代码被执行。 这段视频 很好地解释了 JavaScript 的事件循环,如果您想阅读 此页面。
在浏览器中阻止代码的一个很好的例子是
alert("cannot do anything until you click ok");
。警报会阻止所有内容,用户甚至无法滚动或单击页面中的任何内容,并且您的代码也会阻止执行。在控制台中运行它,您就会明白我所说的阻塞是什么意思。
当您想做一些需要时间的事情时,这会产生问题。在其他框架中,您会使用线程或进程,但在 JavaScript 中没有这样的东西(从技术上讲,节点中有 web worker 和 fork,但这是另一回事,通常比使用异步 api 复杂得多)。
因此,当您想发出 http 请求时,您可以使用
fetch
但是获取需要一些时间才能完成,并且您的函数不应阻塞(必须尽快返回一些东西)。这就是 fetch 返回承诺的原因。请注意,fetch 是由浏览器/节点实现的,并且确实在另一个线程中运行,只有您编写的代码在一个线程中运行,因此开始大量承诺只运行您编写的代码不会加快任何速度,但会并行调用本机异步 api。
在 promises 异步代码使用回调或返回可观察对象(如 XmlHttpRequest)之前,让我们介绍一下 promises,因为您无论如何都可以将更传统的代码转换为 promise。
promise 是一个对象,它有一个
then
函数(以及一堆对 then 有用但作用相同的东西),这个函数有 2 个参数。将回调转换为承诺。
传统的 api(尤其是 nodejs api)使用回调:
这使得程序员很难以线性方式(从上到下)捕获错误或处理返回值。通过错误处理(无法阅读)尝试并行或限制并行地做事情变得更加不可能。
您可以使用
new Promise
异步等待
这就是所谓的承诺语法糖。它使 promise 消费函数看起来更传统,更易于阅读。也就是说,如果你喜欢编写传统代码,我认为编写小函数更容易阅读。例如,你能猜出这是做什么的吗?:
无论如何;足够的咆哮。重要的部分是要了解
async await
实际上并没有启动另一个线程,async
函数总是返回一个承诺和await
实际上没有阻塞或等待。它是someFn().then(result=>...,error=>...)
的语法糖,看起来像:示例始终显示
try catch
但您不需要这样做,例如:抛出的任何错误或
await
返回被拒绝的承诺将导致异步函数返回被拒绝的承诺(除非您尝试捕获它)。很多时候希望让它失败并让调用者处理错误。当您希望 promise 成功并为被拒绝的 promises 提供特殊值时,可能需要捕获错误,以便您可以稍后处理它,但 promise 在技术上不会拒绝,因此将始终解决。
一个例子是
Promise.all
,它接受一组承诺并返回一个新的承诺,该承诺解析为一组已解决的值 或在其中任何一个拒绝时拒绝。您可能只想取回所有承诺的结果并过滤掉被拒绝的: