【问题标题】:How are Promises implemented in Javascript without threadsPromises 如何在没有线程的 Javascript 中实现
【发布时间】:2014-09-24 19:30:30
【问题描述】:

最近,我看到了 Promises 的概念在 AngularJS 和 JQuery 中实现。

我已经在 J​​ava 中看到了 Futures 的实现,如下面的代码所示,但这需要在语言/平台中存在线程池的概念。但是,Javascript 中没有这样的线程概念。那么 Javascript 中的 Promise 是如何实现的呢?

public class Futures1 {

    private static final ExecutorService pool = Executors
            .newFixedThreadPool(10);

    public static void main(String[] args) {

        Future<String> contentsFuture = null;
        try {
            contentsFuture = startDownloading(new URL("http://www.example.com"));
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        // other computation
        try {
            final String contents = contentsFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

    }
    
    public static Future<String> startDownloading(final URL url) {
        return pool.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                try (InputStream input = url.openStream()) {
                    return IOUtils.toString(input, StandardCharsets.UTF_8);
                }
            }
        });
    }
}

【问题讨论】:

  • 不确定我是否明白这个问题,但 javascript 通常是单线程的,但某些本地方法除外,例如 ajax、webworkers、超时、websockets 以及包括 Promises 在内的越来越多的新方法。换句话说,native 方法不一定要遵守单线程的规则
  • Javascript 使用事件是非同步的——事件不需要线程,但可以轻松实现承诺。事件存在于单核 8088 中,当时被称为“硬件中断”
  • 并发!=并行。就这么简单,代码是交错的。
  • 这样想——promise 不提供异步,而是为处理其他方式提供的异步提供语法便利。
  • 承诺是implemented in terms of callbacks。那么回调是如何异步的呢?好吧,that's a different question

标签: javascript promise


【解决方案1】:

我认为 Roamer-1888 回复 OP 的评论可能是将其他答案中的信息提炼成易于理解的内容的关键。

这样想——promise 不提供异步,但它是处理其他方式提供的异步的语法便利。 - Roamer-1888

JS 是单线程的,但是 JS 运行的环境可以利用其他线程。以浏览器为例:JS 可以访问 Web API(例如fetch),允许它通过浏览器引擎在单线程 JS 环境之外执行工作,无论是网络请求,由网络工作者处理的东西,无论如何。 Promise 只是跟踪这些操作并在它们完成后接收它们的结果。

【讨论】:

    【解决方案2】:

    Promise 是为了帮助管理异步操作而发明的。 Promise 本身不需要线程来做到这一点。它们本质上是为异步操作提供簿记的对象——为状态转换保留状态标志、结果值和侦听器。这些都是使用常规单线程 Javascript 可以轻松完成的事情。

    因此,只要您有异步操作(无论这些操作是如何实现的),您就可以从 Promise 中受益,并且不需要线程来实现它们。

    您在 Java 代码中看到的是有助于在单独的线程中运行常规任务的代码(为同步操作提供一些异步类型的行为)。这不是承诺的作用。因此,如果您的环境中已经有异步操作,则不需要此类代码即可使用 Promise。

    Javascript 中的异步事物示例几乎是您感兴趣的任何事物,实际事件将在未来某个时间发生,并且其他代码可以在该事件触发之前运行。在浏览器的 Javascript 环境中,这包括诸如setTimeout()、键盘事件、鼠标事件、ajax 完成回调等……这些都是异步事件。您注册了对它们的兴趣(通过注册事件侦听器或将回调传递给某个函数)。在 Javascript 实现内部,可能有线程使这些异步事件工作,但这些线程不需要直接向程序员公开,异步功能就在那里。例如,请参阅this post,了解 Javascript 如何在其他 Javascript 事物运行时设法在后台运行 ajax 调用。您只需要知道,您的回调函数将在未来某个不确定的时间被调用。

    因此,在 Javascript 中,promise 用于管理环境中已经存在的异步操作。它们不用于使非异步事物变为异步(您需要线程才能做到这一点)。

    请记住,Promise 本身只是监控工具,用于监控现有的异步操作。除了.then() 之外,Promise 本身实际上并不是异步的,它可以使用内置 API 实现,例如 setTimeout()setImmediate()nextTick()。 Promise 不需要它们自己的本机代码或线程。事实上,如果你愿意,你可以用普通的单线程 Javascript 编写一个 Promise 实现。

    【讨论】:

    • 你对最后一句话有很好的资源/参考吗?有兴趣了解更多
    • @aks。 - 有许多仅用 Javascript 编写的 3rd 方承诺库,例如 QBluebird。 Bluebird 是我选择的 promise 库。
    • 我的意思是关于实现你自己的文章。我已经对蓝鸟发誓:)
    • @aks。 - 我不确定你在找什么。您可以在我之前评论中的链接中查看 Q 和 Bluebird 的源代码。怎么做都在那里。简而言之,Promise 库只是一个遵循一些标准化规则的通知系统。您注册了对某个事件的兴趣,如果/当该事件发生时,它会调用您的回调。
    【解决方案3】:

    JavaScript 层下的浏览器编写的本机代码倾向于非常整齐地实现线程。事实证明,这通常就是您所需要的。在 JavaScript 中执行实际的计算工作往往不需要 Promise(尽管工作人员使这更容易),但用于加载外部资源并在完成后获取回调。 JS Promises 只是为“image.onLoad”之类的函数分配回调,并检查是否通知另一个函数。

    Hogan 可能对它进行了最好的总结 - 基于事件的编程。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-17
      • 1970-01-01
      • 2014-08-19
      • 1970-01-01
      • 2012-09-28
      • 1970-01-01
      相关资源
      最近更新 更多