如何在JavaScript异步函数中返回承诺?为什么异步方法不包含返回的承诺?

发布于 2025-01-27 17:19:47 字数 1751 浏览 1 评论 0原文

我有以下代码。我的目的是启动一个任务(涉及几个等待调用,然后才能真正启动)。当它完成启动时,我需要更新UI,以告诉用户任务已经启动并正在等待结果。结果将稍后出现,因此我想返回另一个Promise,因此该应用程序知道何时更新UI并返回结果:

async function startSomethingAsync() {
    await new Promise(r => globalThis.setTimeout(r, 1000));
    console.log("Task started");

    const finishedPromise = new Promise<number>(r => {
        globalThis.setTimeout(() => r(100), 3000);
    });
    return finishedPromise;
}

(async() => {
    // resultPromise is number instead of Promise<number>
    const resultPromise = await startSomethingAsync();

    // My intention: update UI after starting the task
    console.log("Task started");

    // Then when the result is there, do something else
    // Currently this is not compiled because resultPromise is not a Promise
    resultPromise.then(r => console.log("Result arrived: " + r));
})();

但是上面的代码不起作用。不知何故,resultpromise号码(解决后100),而不是Promise&lt; numbers&gt;。 TypeScript还识别startemethingAsync返回promise&lt; number&gt;而不是promise&lt; promise&lt;

为什么会发生这种情况? async方法不应该在返回的承诺之外包裹另一个承诺吗?我如何实现自己要做的事情,这是一种“好”模式?


我什至尝试自己包装它:

async function startSomethingAsync() {
    await new Promise(r => globalThis.setTimeout(r, 1000));
    console.log("Task started");

    const finishedPromise = new Promise<Promise<number>>(r1 => r1(new Promise<number>(r2 => {
        globalThis.setTimeout(() => r2(100), 3000);
    })));
    return finishedPromise;
}

该功能仍然返回Promise&gt;,而不是Promise&lt; Promise&lt; number&gt;&gt;

I have the following code. My intention is to start a task (which involves a few await calls before it can actually start). When it finishes starting it, I need to update the UI to tell user that the task has started and is waiting for result. Result will come in later so I want to return another Promise so the app knows when to update the UI with returned result:

async function startSomethingAsync() {
    await new Promise(r => globalThis.setTimeout(r, 1000));
    console.log("Task started");

    const finishedPromise = new Promise<number>(r => {
        globalThis.setTimeout(() => r(100), 3000);
    });
    return finishedPromise;
}

(async() => {
    // resultPromise is number instead of Promise<number>
    const resultPromise = await startSomethingAsync();

    // My intention: update UI after starting the task
    console.log("Task started");

    // Then when the result is there, do something else
    // Currently this is not compiled because resultPromise is not a Promise
    resultPromise.then(r => console.log("Result arrived: " + r));
})();

However the above code does not work. Somehow resultPromise is number (100 after resolved) instead of Promise<number>. TypeScript also recognize startSomethingAsync returning Promise<number> instead of Promise<Promise<number>>.

Why is this happening? Shouldn't the async method wrap another Promise outside of the returned Promise? How do I achieve what I am trying to do and is it a "good" pattern?


I even tried wrapping it by myself:

async function startSomethingAsync() {
    await new Promise(r => globalThis.setTimeout(r, 1000));
    console.log("Task started");

    const finishedPromise = new Promise<Promise<number>>(r1 => r1(new Promise<number>(r2 => {
        globalThis.setTimeout(() => r2(100), 3000);
    })));
    return finishedPromise;
}

The function still returns Promise<number> instead of Promise<Promise<number>>.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

極樂鬼 2025-02-03 17:19:47

正如我们在评论中讨论的那样 - 您必须将结果承诺包裹在功能或对象中。

async function startSomethingAsync() {
  await new Promise(r => setTimeout(r, 1000));

  const finishedPromise = new Promise(r => {
    setTimeout(() => r(100), 3000);
  });
  return () => finishedPromise;
}

(async () => {
  const resultPromise = await startSomethingAsync();
  resultPromise().then((r) => console.log("Result arrived: " + r));
})();

As we discussed in the comments - you have to wrap the resulting promise in a function or an object.

async function startSomethingAsync() {
  await new Promise(r => setTimeout(r, 1000));

  const finishedPromise = new Promise(r => {
    setTimeout(() => r(100), 3000);
  });
  return () => finishedPromise;
}

(async () => {
  const resultPromise = await startSomethingAsync();
  resultPromise().then((r) => console.log("Result arrived: " + r));
})();

莫多说 2025-02-03 17:19:47

为什么会发生这种情况?异步方法不应该在返回的承诺之外包裹另一个承诺吗?

否。如果您从async函数中返回承诺,则通过调用async函数创建的承诺只是 解决了该承诺为了解决并将其成就或拒绝作为自己的诺言的其他诺言; how-to-talk-about-promises/“ rel =“ nofollow noreferrer”>在这里)。它没有建立承诺,以实现承诺。承诺永远不会通过承诺实现(无论async函数如何)。

我如何实现我要做的事情,这是一种“好”模式?

如果要使用startemethingSync的承诺,请不要等待 IT:

(async() => {
    const resultPromise = startSomethingAsync();
    //                    ^−−− no `await`

    // My intention: update UI after starting the task
    console.log("Task started");

    // Wait until it's finished
    const r = await resultPromise;
    //        ^−−− *now* we `await` await it
    console.log("Result arrived: " + r);
})();

请注意,如果startermethingsomeNagync在启动其启动之前具有异步延迟工作(这是非常不寻常的),“任务启动”日志将在异步延迟发生之前发生。如果您不想要它,请将该部分与功能分开,以便您可以在中间登录“任务启动”。例如,您可能具有执行初始位的单独函数,然后将任何内容从开始> startemethingsasync

(async() => {
    const resultPromise = startSomethingAsync(await theStuffBefore());
    //                                        ^^^^^

    // My intention: update UI after starting the task
    console.log("Task started");

    // Wait until it's finished
    const r = await resultPromise;
    //        ^−−− *now* we `await` await it
    console.log("Result arrived: " + r);
})();

或者,如果您的过程是多步骤,则每个步骤都可以返回函数(或下一步的方法):(

(async() => {
    const nextStep = await startSomethingAsync();
    const resultPromise = nextStep(); // No `await`

    // My intention: update UI after starting the task
    console.log("Task started");

    // Wait until it's finished
    const r = await resultPromise;
    console.log("Result arrived: " + r);
})();

实际上,这与使用异步生成器非常相似。)

这些都是主题上的变化,但是外卖消息是,另一个承诺永远无法实现承诺。您会注意到,您从获得的函数的标准名称resolverecount,而不是实现 >和<代码>拒绝。这是因为当您将承诺传递到resolve中(或从async函数返回一个,这是同一件事)时,它 sesolves 承诺 其他诺言,而不是通过其他诺言实现诺言。

Why is this happening? Shouldn't the async method wrap another Promise outside of the returned Promise?

No. If you return a promise from an async function, the promise created by calling the async function is just resolved to that promise (it waits for that other promise to settle and takes its fulfillment or rejection as its own; more on promise terminology on my blog here). It doesn't set up the promise to be fulfilled with a promise. A promise is never fulfilled with a promise (regardless of async functions).

How do I achieve what I am trying to do and is it a "good" pattern?

If you want to use the promise from startSomethingAsync, simply don't await it:

(async() => {
    const resultPromise = startSomethingAsync();
    //                    ^−−− no `await`

    // My intention: update UI after starting the task
    console.log("Task started");

    // Wait until it's finished
    const r = await resultPromise;
    //        ^−−− *now* we `await` await it
    console.log("Result arrived: " + r);
})();

Note that if startSomethingAsync has an asynchronous delay before it starts its work (which would be highly unusual), the "Task started" log will occur before that asynchronous delay occurs. If you don't want that, separate out that part from the function so you can get your "Task started" logging in the middle. For instance, you might have a separate function that does the initial bit, and then you pass anything from that into startSomethingAsync:

(async() => {
    const resultPromise = startSomethingAsync(await theStuffBefore());
    //                                        ^^^^^

    // My intention: update UI after starting the task
    console.log("Task started");

    // Wait until it's finished
    const r = await resultPromise;
    //        ^−−− *now* we `await` await it
    console.log("Result arrived: " + r);
})();

Alternatively, if your process is multi-step, each step could return a function (or an object with methods) for the next step:

(async() => {
    const nextStep = await startSomethingAsync();
    const resultPromise = nextStep(); // No `await`

    // My intention: update UI after starting the task
    console.log("Task started");

    // Wait until it's finished
    const r = await resultPromise;
    console.log("Result arrived: " + r);
})();

(In fact, that's very similar to using an async generator.)

These are all variations on a theme, but the take-away message is that a promise can never be fulfilled with another promise. You'll notice that the standard name for the functions you get from new Promise are resolve and reject, not fulfill and reject. That's because when you pass a promise into resolve (or return one from an async function, which is effetively the same thing), it resolves the promise to that other promise, rather than fulfilling the promise with that other promise.

冰雪梦之恋 2025-02-03 17:19:47

所有async功能隐式返回承诺。如果您明确归还承诺,则不会包装它。

如果承诺另一个承诺,他们嵌套的诺言是扁平的 -

(new Promise(r =>
  r(new Promise(r2 =>
    r2("hello")
  ))
))
.then(console.log).catch(console.error)

All async functions implicitly return a promise. If you explicitly return a promise, it will not be wrapped.

If a promise resolves another promise, they nested promises are flattened -

(new Promise(r =>
  r(new Promise(r2 =>
    r2("hello")
  ))
))
.then(console.log).catch(console.error)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文