js文件引用方式及其同步执行与异步执行

本文详细解析了JavaScript在不同场景下的加载与执行机制,包括同步与异步加载的区别,以及通过不同方式引入JS文件时的执行顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp74

 

任何以appendChild(scriptNode) 的方式引入的js文件都是异步执行的 (scriptNode 需要插入document中,只创建节点和设置 src 是不会加载 js 文件的,这跟 img 的与加载不同 )   
html文件中的<script>标签中的代码或src引用的js文件中的代码是同步加载和执行的
html文件中的<script>标签中的代码使用document.write()方式引入的js文件是异步执行的
html文件中的<script>标签src属性所引用的js文件的代码内再使用document.write()方式引入的js文件是同步执行的

1、

<script>

//同步加载执行的代码

</script>

2、

<script src="xx.js"></script>  //同步加载执行xx.js中的代码

3、

<script>

document.write('<script src="xx.js"><\/script>');   //异步加载执行xx.js中的代码

</script>

4、

<script src="xx.js"></script>

xx.js中有下面代码:

document.write('<script src="11.js"><\/script>');   

document.write('<script src="22.js"><\/script>');   

则xx.js和11.js、22.js 都是同步加载和执行的。

如果 xx.js 以插入方式异步加载,则 11.js 和 22.js 仍然是同步加载的(异步中的同步,即,这2个文件的加载是分先后次序的)

测试:在11中 alert, 22中 document.write() ,可以看到 22中写入语句被阻塞

5、

下面这种方式,xx.js会在appendChild执行之后异步加载执行

var script = document.createElement("script");

script.setAttribute("src","xx.js");

documenrt.getElementsByTagName("head")[0].appendChild(script);

   

 

一个加载 js 文件的 函数:

var loadJS = function(url,callback){  var head = document.getElementsByTagName('head')[0],   script = document.createElement('script');    script.src = url;   script.type = "text/javascript";   head.appendChild( script);

   script.onload = script.onreadystatechange = function(){

//script 标签,IE 下有 onreadystatechange 事件, w3c 标准有 onload 事件     

//这些 readyState 是针对IE8及以下的,W3C 标准因为script 标签没有这个 onreadystatechange 所以也不会有 this.readyState , 

// 好在文件加载不成功 onload 不会执行,(!this.readyState) 是针对 W3C标准的

if ((!this.readyState) || this.readyState == "complete" || this.readyState == "loaded" ){ 

         callback();      }else{          alert("can not load the js file")      }   }}

 

对于第4点的测试(其中插入 alert 很容易看到加载时阻塞的)

tryjs.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script src="tryjs.js" 
onload="if(!document.all){console.log('outer js callback, not IE');}" 
onreadystatechange="console.log('outer js callback ',this.readyState,' IE');"></script>


<body>

</body>
</html>

 

tryjs.js

console.log('write begin');
document.write('<script src="try.1.js" onreadystatechange="console.log(\'file 1 callback \',this.readyState,\' IE\');" onload="if(!document.all){console.log(\'file 1 callback,NOT IE \');}"><\/script>');
document.write('<script src="try.2.js" onreadystatechange="console.log(\'file 2 callback \',this.readyState,\' IE\');" onload="if(!document.all){console.log(\'file 2 callback,NOT IE \');}"><\/script>');
console.log('write finished');

try.1.js
console.log('loadjs 1 begin');
console.log('loadjs 1 finished');

 

try.2.js

console.log('loadjs 2 begin');
console.log('loadjs 2 finished');

 

测试结果(file 2 和 file 1 的 callback complete 在IE7\8\9次序不确定

IE 7:
日志: outer js callback loading IE 
日志: outer js callback loaded IE 
日志: write begin 
日志: write finished 
日志: outer js callback complete IE 
日志: file 1 callback loading IE 
日志: file 2 callback loading IE 
日志: loadjs 1 begin 
日志: loadjs 1 finished 
日志: loadjs 2 begin 
日志: loadjs 2 finished 
日志: file 2 callback complete IE 
日志: file 1 callback complete IE 


IE8:
日志: outer js callback loading IE 
日志: outer js callback loaded IE 
日志: write begin 
日志: write finished 
日志: outer js callback complete IE 
日志: file 1 callback loading IE 
日志: file 2 callback loading IE 
日志: loadjs 1 begin 
日志: loadjs 1 finished 
日志: loadjs 2 begin 
日志: loadjs 2 finished 
日志: file 2 callback complete IE 
日志: file 1 callback complete IE 

IE9:
日志: write begin 
日志: write finished 
日志: outer js callback complete IE 
日志: file 1 callback loading IE 
日志: file 2 callback loading IE 
日志: loadjs 1 begin 
日志: loadjs 1 finished 
日志: loadjs 2 begin 
日志: loadjs 2 finished 
日志: file 1 callback complete IE 
日志: file 2 callback complete IE 



FIREFOX:
write begin 
write finished
outer js callback, not IE
loadjs 1 begin
loadjs 1 finished
file 1 callback,NOT IE
loadjs 2 begin
loadjs 2 finished
file 2 callback,NOT IE


CHROME:
write begin     
write finished    
outer js callback, not IE    
loadjs 1 begin    
loadjs 1 finished    
file 1 callback,NOT IE 
loadjs 2 begin    
loadjs 2 finished    
file 2 callback,NOT IE 

<think>好的,我现在需要帮助用户理解异步执行的概念及其在编程中的应用,以及不同的实现方式。用户之前提到了虚幻引擎中的.generated.h文件,但现在的问题转向了异步编程,特别是JavaScript和Java中的实现方法。不过根据当前的问题,用户现在想了解更一般的异步执行概念和实现方式。 首先,我需要回顾用户提供的引用内容。引用1和2提到了JavaScript中的异步编程方法,如回调函数、Promise、async/await,以及Fetch API和Axios。引用3讨论了线程池实现异步编程时主线程阻塞的问题。引用4和5涉及Java中的异步实现方式,如基础线程、CompletableFuture、Executor框架、响应式编程和消息队列。这些引用提供了不同语言中的异步编程方法,但用户的问题更偏向于概念和应用,需要综合这些信息。 用户的问题分为两个部分:异步执行的概念和在编程中的应用,以及实现方式。因此,我需要先解释异步执行的基本概念,然后讨论其应用场景,最后列举不同编程语言中的实现方式,并举例说明。 首先,异步执行的概念。异步指的是任务启动后,主线程不需要等待其完成,可以继续执行其他操作。这同步相反,同步会阻塞主线程直到任务完成。异步提高了程序的响应性和资源利用率,特别是在处理I/O密集型或高延迟任务时。 接下来是应用场景。根据引用中的例子,比如前端中的网络请求(Fetch API、Axios)、文件操作,后端中的数据库访问、分布式任务,以及多线程环境中的任务调度。这些场景中异步执行能有效提升性能。 然后是实现方式。需要涵盖不同语言的方法,如JavaScript中的回调和Promise,Java中的线程和CompletableFuture,以及通用的线程池、事件循环、消息队列等。同时,要比较不同方式的优缺点,比如回调地狱问题,Promise链式调用的改进,async/await的同步写法优势,Java中CompletableFuture的链式处理能力,以及响应式编程和消息队列的适用场景。 需要注意用户可能混淆不同语言的实现,比如JavaScript是单线程的,依靠事件循环,而Java是多线程的,使用线程池。因此,在解释时要区分不同环境下的机制。同时,需要举例说明,帮助用户理解抽象概念。 还需要检查是否有遗漏的实现方式,比如Python的asyncio,或者C#的async/await,但根据引用内容,用户主要关注JavaScript和Java,所以可以集中在这些语言上。不过,为了全面性,可以简要提及其他语言的方法。 最后,确保回答结构清晰,分点说明,每个部分有例子和优缺点分析,并引用提供的资料中的例子,如引用5中的CompletableFuture方法。同时,在末尾添加相关问题,引导用户深入探讨,如性能优化、错误处理、不同语言对比等。 总结回答的结构: 1. 异步执行的概念 2. 应用场景 3. 实现方式(分语言和通用方法) 4. 优缺点分析 5. 示例代码 6. 相关问题 需要确保使用正确的中文术语,避免技术术语错误,并且例子要简明,符合用户提供的引用中的代码风格。同时,注意用户可能对某些概念不熟悉,如事件循环、线程池等,需要适当解释。</think>#### 一、异步执行的核心概念 **异步执行是一种非阻塞式编程范式**,其核心特征是:**任务发起后无需等待结果即可继续执行后续逻辑**,任务完成后通过回调、通知等方式返回结果。其数学本质可描述为: $$ \text{程序流}(t) = \begin{cases} \text{主线程任务序列} & t \in [0, T_1) \cup [T_2, \infty) \\ \text{异步任务执行} & t \in [T_1, T_2) \end{cases} $$ 其中$T_1$为异步任务启动时间点,$T_2$为完成时间点[^3]。 #### 二、典型应用场景 | 场景类型 | 具体案例 | 异步实现价值 | |----------------|-----------------------------|----------------------------------| | **I/O密集型** | 文件读写、数据库查询 | 避免阻塞CPU资源 | | **网络通信** | HTTP请求、WebSocket消息 | 提升系统吞吐量(如Axios库)[^2] | | **高延迟操作** | 图像处理、机器学习推理 | 隐藏延迟提升用户体验 | | **事件驱动** | GUI点击响应、游戏逻辑 | 确保主线程流畅性 | #### 三、实现方式技术对比 ##### 1. 基础模式 - **回调函数(Callbacks)** ```javascript fs.readFile('data.txt', (err, data) => { if (err) throw err; console.log(data); // 异步完成后的处理 }); ``` *优点*:实现简单 *缺点*:嵌套层级过深导致"回调地狱"[^2] - **事件监听** ```java button.addActionListener(e -> { executor.submit(() -> processClick()); // 异步执行 }); ``` ##### 2. 结构化模式 - **Promise(JavaScript)** ```javascript fetch('/api/data') .then(response => response.json()) .then(data => render(data)) .catch(error => handleError(error)); ``` 通过链式调用解决回调嵌套问题[^1] - **async/await(ES7+)** ```javascript async function loadData() { try { const response = await fetch('/api/data'); const data = await response.json(); return data; } catch (error) { console.error('Fetch failed:', error); } } ``` 以同步写法实现异步逻辑[^1] ##### 3. 并发框架 - **CompletableFuture(Java)** ```java CompletableFuture.supplyAsync(() -> fetchDataFromDB()) .thenApply(data -> transformData(data)) .thenAccept(result -> sendToUI(result)) .exceptionally(ex -> logError(ex)); ``` 支持任务链式组合异常传递[^5] - **响应式编程(ReactiveX)** ```java Observable.fromCallable(() -> intensiveComputation()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> updateUI(result)); ``` ##### 4. 系统级方案 - **线程池(Executor框架)** ```java ExecutorService pool = Executors.newFixedThreadPool(4); pool.execute(() -> processTask(task)); ``` 通过资源复用提升性能[^4] - **消息队列(RabbitMQ/Kafka)** ```python channel.basic_publish( exchange='', routing_key='task_queue', body=json.dumps(task_data) ) ``` 实现跨进程异步通信 #### 四、实现方式选择矩阵 | 指标\方案 | 回调函数 | Promise | async/await | 线程池 | 消息队列 | |------------------|----------|---------|-------------|--------|----------| | 代码可读性 | ★☆☆☆☆ | ★★★☆☆ | ★★★★★ | ★★☆☆☆ | ★★★☆☆ | | 错误处理能力 | ★★☆☆☆ | ★★★★☆ | ★★★★★ | ★★★☆☆ | ★★★★☆ | | 跨进程支持 | 不支持 | 不支持 | 不支持 | 有限 | 支持 | | 资源消耗 | 低 | 低 | 低 | 中 | 高 | | 适用规模 | 小任务 | 中小 | 中大型 | 中大型 | 分布式 | #### 五、核心优化策略 1. **背压控制** 在数据生产速率 > 消费速率时,通过反压机制避免内存溢出: ```java Flowable.range(1, 10000) .onBackpressureDrop(item -> logDropped(item)) .observeOn(Schedulers.computation()) .subscribe(...); ``` 2. **超时熔断** 使用Hystrix等工具防止级联故障: ```java @HystrixCommand( fallbackMethod = "fallback", commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="3000") } ) public String callRemoteService() {...} ``` --- **相关问题**: 1. 异步编程中如何避免竞态条件? 2. Node.js事件循环机制如何实现高并发? 3. CompletableFutureRxJava在异步处理上有何本质区别? 4. 异步任务中的上下文传递有哪些最佳实践? 5. 如何设计可靠的异步错误传播机制?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值