< 返回版块

apawn 发表于 2020-12-05 13:14

被一个问题困惑了好久: 在一些业务场景下,一个远端数据可能会被多次使用,比如: get_res() 可以被多次调用,如果已经fetch 成功,就会立即返回,否则就会等待 fetch 结果。 这种写法可以避免回调函数。


const res = fetch("xxxxx");

// get_res 可以被多次调用。
async get_res() {

    await res;
}

但是在rust中,future 文档里写到:一个 future 只能被 poll 一次。 否则有可能会有预期外的情况。 不知道在rust当中,有没有办法做到类似的效果

评论区

写评论
chansia 2020-12-06 18:42

老哥, 你还是没理解啊, 先按你的javascript代码来说:

// 情况一: 
// 这里你多次调用await get_res(), get_res()返回的是不同的Promise object,
// 不是同一个Promise object, 而await后返回的是get_res()调用结果, 是一个值, 
// 不是Promise。如果你说是这种情况, await多次没有没问题。
const value = await get_res();

// 情况二: 
// 这里返回的是一个Promise object
var promise = get_res();

// 因为javascript Promise是一个状态机, 有pending, fulfilled和rejected
// 状态, 当Promise处于fulfilled状态后, await返回的是get_res调用结果
const value1 = await promise;

// promise已处于fulfilled状态, 再次await, 返回的还是同样的值
const value2 = await promise;

// value2与value1相等
console.assert(value1 == value2);

如果你想说的是情况二, 但你不能在rust里这么干, 因为rust有Ownership和Move Sematics:

// 假设函数get_rest signature是这样
async fn get_res() -> SomeResponse {
    todo!()
}

// rust的async函数是"lazy"的, 这里返回的是一个Future, 而不是SomeResponse
let future = get_res();

// await后返回是SomeResponse, 注意这里future已经move
let value = future.await;

// 因为上面的future已经move, 你不能再次await这个future, 否则编译器会直接
// 报错: use of moved value: `future`
let value2 = future.await; // 这里编译无法通过

另外你对rust Future实现和Poll机制不是很了解。

Mike Tang 2020-12-05 21:01

NotReady已经是过期很久的文档了,认准 docs.rs 商标。

Mike Tang 2020-12-05 19:04

而且我不知道你看的啥文档。

Mike Tang 2020-12-05 19:03

再仔细看看文档吧,不要囫囵吞枣。

--
👇
apawn: 文档中写到 fuse就是返回一个 NotReady,而不是立即返回数据 ?

👇
Mike Tang: Rust中早就为咱们考虑好了这种情况,.fuse() 一下就可以。

FusedFuture 查一下。其实就相当于包了一层,防止UB。

作者 apawn 2020-12-05 17:15

好的,谢谢回答 。 贴的代码是js 的, 在 js 里,get_res()返回了一个Promise,这个 Promise 已经被resolve 后,如果继续await,就会直接返回结果。

主要是为了解决一种业务场景,比如需要在passport获取当前用户,passport 提供一个异步函数 async getUser();

上层业务在调用的时候,直接await getUser() 结果即可,不需要判断是否已经ready了,如果没有ready,也不需要再二次请求,而是在第一次请求完成后一起resolve。如果没有ready,就直接返回结果。

这里 rust 的实现可能和js不太一样, 如果 await 一个 future,就相当于 poll 了一次,会额外再发出一次请求。 并且,如果对于已经ready的 future,无法 await 直接获取结果 。

--
👇
chansia: 有些不太明白你想问什么?你这里多次调用get_res没有任何问题,因为每次调用都会产生一个不同future,每一个future都会被Executor正确处理。如果每次请求结果都一样,而你又不想产生多次网络io,你可以把请求结果缓存起来,后续直接访问这个缓存,然后定时更新缓存。另外futures文档说的不是一个future只能poll一次,而是说底层实现Executor时,尽量避免多次低效poll future,而是等到future ready通知后,poll一次就好了

chansia 2020-12-05 16:29

有些不太明白你想问什么?你这里多次调用get_res没有任何问题,因为每次调用都会产生一个不同future,每一个future都会被Executor正确处理。如果每次请求结果都一样,而你又不想产生多次网络io,你可以把请求结果缓存起来,后续直接访问这个缓存,然后定时更新缓存。另外futures文档说的不是一个future只能poll一次,而是说底层实现Executor时,尽量避免多次低效poll future,而是等到future ready通知后,poll一次就好了

作者 apawn 2020-12-05 14:22

文档中写到 fuse就是返回一个 NotReady,而不是立即返回数据 ?

👇
Mike Tang: Rust中早就为咱们考虑好了这种情况,.fuse() 一下就可以。

FusedFuture 查一下。其实就相当于包了一层,防止UB。

Mike Tang 2020-12-05 13:37

Rust中早就为咱们考虑好了这种情况,.fuse() 一下就可以。

FusedFuture 查一下。其实就相当于包了一层,防止UB。

作者 apawn 2020-12-05 13:18

对对对, 就是一个future 在ready 之后,还可以继续poll 多次吗 。简单来说,就是一个 future 被 await 多次

--
👇
Mike Tang: 一个Poll是被 Poll::Ready 一次,不是只能被 poll 一次。

Mike Tang 2020-12-05 13:16

一个Poll是被 Poll::Ready 一次,不是只能被 poll 一次。

1 共 10 条评论, 1 页