【问题标题】:Using resolved promise data synchronously同步使用已解决的承诺数据
【发布时间】:2019-01-02 07:11:53
【问题描述】:

我正在学习 Promise,并且我绝对想在继续之前确保我了解它们的用途。我正在使用一个用于在线服务的库,该库具有返回承诺的功能。

我阅读的几乎所有示例都使用链接的then() 函数中的解析数据

const result = Library.functionReturningAPromise()
result.then(function(res) {
    const obj = new Example(res)
    return obj
}).then(function(ob) {
    // do the rest of the logic within these then() functions
})

或在async 函数中使用解析的数据

async function test() {
    const result = await Library.functionReturningAPromise()
    const obj = new Example(result)

    // do the rest of the logic
}

我想知道是否有任何方法可以在“正常”同步代码中使用已解决的承诺中的数据

 const result = Library.functionReturningAPromise()

 // do something to resolve the promise

 const obj = new Example(result)

或者,如果您需要始终“包装”所有您在 async 函数中使用已解决承诺中的数据的逻辑。

【问题讨论】:

  • “我想知道是否有任何方法可以在“正常”同步代码中使用已解决的承诺中的数据“没有。
  • 不可能。无论您是否通过 Promise 访问结果都无所谓,当它异步可用时,这意味着它现在(同步)不可用 - 也不能 - 可用。
  • Library.functionReturningAPromise().then(...等会解决你的问题。您可以根据需要向 Promise 添加任意数量的“then”(或任何其他有效的)回调。
  • 据我理解的问题,不是从异步函数中获取数据,而是在异步位之后处理数据。 .then-chaining 的异步方式和一般的编码风格如果你不习惯的话会让人感到困惑。即使你是,偶尔每个人都可能会迷失在一个.then太多:)
  • 不,但是从技术上讲,您不必等待承诺立即解决,您可以随时致电 thencatch(或 async try/catch)承诺承诺,甚至多次并以不同的方式对其进行操作。

标签: javascript asynchronous promise synchronous


【解决方案1】:

如果您想“离开”异步上下文,您可以等待 Promise 完成,然后 then() 调用您的“常规”函数之一作为回调。

我可以理解异步编码风格可能非常混乱,但实际上这正是then(function () {}) 所做的。

如果您更容易理解或使用这种编码风格,那么它在功能上是等效的。

function first() {
    console.log("sync!");
    doAsync();
}

function doAsync() {
    console.log("now we're async!");
    Library.functionReturningAPromise().then(second);
}

function second() {
    console.log("sync again!");
    // ...
}

【讨论】:

  • "只是等待 Promise 完成"不会离开异步上下文。
  • @Bergi 请注意leave 周围的引号。也请考虑我对这个问题的评论。希望说明清楚:) 我并不是说您实际上可以离开异步上下文,根本没有回头路。但是您可以使用这种方法“逃避”异步及其相关样式(thenawaitasync)。
  • 我认为这种编码风格在功能上并不等同,使用 Promises 管理异步代码的主要目的之一是错误处理管道。提议的方法将 Promises 变成了美化的回调。虽然习惯于编写同步代码时可能更容易理解,但值得花时间了解 Promise 的工作原理,以便利用它们的基本功能。
【解决方案2】:

我想知道是否有任何方法可以在“正常”同步代码中使用已解决的承诺中的数据

在处理异步响应时,无法编写完全同步的代码。一旦任何操作是异步的,您就必须使用异步技术处理响应,并且不能对其进行同步编程。你必须学会​​异步编程。

您显示的两个选项(.then()async/await)是您处理返回的承诺的两个选择。

或者,如果您需要始终“包装”所有使用异步函数中已解析承诺中的数据的逻辑。

如果你想使用await 来编写看起来同步的代码来处理promise,那么所有这些代码都必须在async 函数中。而且,一旦您离开该函数的范围(例如想要返回一个值),您就会从 async 函数返回一个承诺,并且必须再次处理该承诺。

没有办法解决这个问题。这只是一个必须在 Javascript 中学习的东西。一段时间后它会成为第二天性。


您似乎知道,您可以使用asyncawait 来获得一些看起来同步的逻辑流程,但是在执行此操作时需要确保您理解一些事情。从你的例子:

async function test() {
    const result = await Library.functionReturningAPromise()
    const obj = new Example(result);

    // do the rest of the logic
}
  1. async 声明的所有函数都返回一个promise。这是您从他们那里获得的唯一一种返回值。如果调用者正在寻找返回值或想知道异步操作何时完成或正在寻找错误,他们必须使用返回的 Promise 和 .then().catch() 或在 async 内再次使用 await功能。
  2. 如果您等待的 Promise 被拒绝,它实际上会抛出并中止函数的其余执行,然后返回拒绝返回的 Promise。
  3. 如果您希望执行流程在被拒绝的 Promise 上中止并且调用者将处理拒绝,那么这一切都很好。
  4. 但是,如果呼叫者没有处理拒绝,则需要有人处理。如果您使用 await 并且需要在本地处理拒绝,则需要将它们包装在 try/catch 中(与同步异常非常相似的语法)。

下面是一个使用try/catchawait 在本地处理错误的示例:

async function test() {
    try {
        const result = await Library.functionReturningAPromise();
        const obj = new Example(result);

        // do the rest of the logic
    } catch(e) {
        // handle promise rejection here
    }
}

以下是调用者处理错误的示例:

async function test() {
    const result = await Library.functionReturningAPromise();
    const obj = new Example(result);

    // do the rest of the logic
}

test().then(() => {
    console.log("all done");
}).catch(err => {
    console.log(err);
});

【讨论】:

  • 真的没有办法在不使用回调的情况下执行同步/阻塞代码吗?
  • @NathanL - 我不确定你在问什么,因为你可能混淆了术语。在像 node.js 这样的环境中,有异步 API 和同步 API。文件系统 API 等一些 API 允许您选择要使用阻塞/同步或非阻塞/异步的版本。如果您选择异步 API,则没有某种回调或承诺就无法使用它。您可以使用带有承诺的await 来编写看起来更同步的代码,尽管它仍然是异步的。对于网络等其他 API,只有非阻塞/异步 API。
  • @NathanL - 所以你不能用 Javascript 中的异步 API 编写纯粹的同步代码。做不到。这是一种不同的编程方式。如果您想要阻塞/同步网络 API,那么您需要选择不同的编程环境。
  • 抱歉,澄清一下:您不能以阻塞方式运行其他异步方法,这样脚本在方法完成之前不会继续执行?
  • @NathanL - 是的,这是正确的。异步、非阻塞代码是非阻塞的,你不能让它阻塞,你也不能让它变成一个同步操作。您可以使用 Promise 来编写看起来更简洁、看起来更同步的代码,使用 async/await 最终仍会返回一个 Promise,而调用者必须使用 await.then() 才能获得最终结果。
【解决方案3】:

可以使用async/awaitthen() 的组合来同步编程如果您将它们的使用包装在一个返回默认值的函数中。

这是一个使用记忆化来返回结果或在需要时查询它的示例:

const scope = {
  // someResult is wrapped in an object so that it can be set even if it's a primitive
  // (e.g. number / string).
  someResult: null,
};

function getResult() {
  if (scope.someResult !== null) {
    // If we already have a result, return it. No need to query Library again.
    return scope.someResult;
  }

  // Note: This example doesn't implement debouncing (avoiding repeated Library queries while
  // Library is still being queried).
  queryLibrary().then(result => {
    if (scope.someResult === null) {
      // If the Library call succeeded and we haven't already stored its result, store it.
      scope.someResult = result;
      console.log('We have a result now!', getResult());
    }
  });

  return scope.someResult;
}

async function queryLibrary() {
  const result = await functionReturningAPromise();
  // Do some stuff with result in a nice async / await way.
  result.push(9000, 1336);
  return result;
}

/** This is some complicated async function from Library. */
async function functionReturningAPromise() {
  return [419, 70];
}

// Calling getResult() will return either null or a list of numbers.
console.log('Do we have a result yet?', getResult());

【讨论】:

  • 看来范围部分有错误。 someResult: null as number[] |空,
  • 谢谢,Sparc。我误以为这是一个 TypeScript 问题。
猜你喜欢
  • 2018-04-02
  • 1970-01-01
  • 2016-05-30
  • 2020-11-21
  • 2021-05-25
  • 1970-01-01
  • 1970-01-01
  • 2018-05-06
相关资源
最近更新 更多