JavaScript:异步函数的应用

本文介绍了JavaScript中异步函数的应用,包括如何模拟Java的Thread.sleep()功能,实现平行执行以提高效率,以及利用异步函数模拟期约连锁操作。通过示例展示了在单线程环境中,平行执行可以减少任务间的等待时间,提高程序执行效率。

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

异步函数的应用


主要参考资料:

  • 《JavaScript 高级程序设计(第4版)》- P356(381/931)

模拟功能 sleep()

在异步函数中,可以实现类似 Java 中如 Thread.sleep() 之类的、能够暂时停止执行当前代码的功能。

示例:

  • 在异步函数中实现暂时停止执行当前代码的的功能。
    async function sleep(delay) {
    	return new Promise(
    		(resolve) => {
    			setTimeout(resolve, delay)
    		}
    	)
    }  // 创建一个一定时间后落定为解决的期约
    
    async function canPause() {
    	const t = Date.now()
    	await sleep(1000)  // 暂停 1s 后,恢复执行
    	const t_01 = Date.now()
    	console.log('paused time:', t_01 - t)
    }
    
    canPause()
    
    // 输出:
    // paused time: 1002
    

模拟平行执行

平行执行是相对于顺序执行的。

顺序执行

任务按顺序执行,只有上一个任务执行完毕后,才开始执行下一个任务。

平行执行

任务的执行没有顺序,是平行的(多线程)/ 随机的(单线程),不需要等待上一个任务执行完毕后,才开始执行下一个任务。

使用异步函数实现平行执行:

在异步函数中按顺序等待各个表达式的值,并在开始等待之前异步开启各个表达式主要的求值。

单线程中的平行执行:

在 JavaScript 这种单线程语言上使用平行执行,主要是减少下一个任务等待执行的等待时间。

从线程的实际执行的性能(线程的使用时间)上看,使用平行执行与使用顺序执行的几乎是没有差别。

因为当某个任务处于等待状态时,这个任务并不总是占用线程的,JavaScript 引擎会安排其它任务使用线程。

平行执行与顺序执行之间细微的性能差别来自于,顺序执行需要更多地进入线程来确定表达式是否已经完成求值,这会消耗一些性能。

所以平行执行,主要是一种寻求能够合理调度任务执行顺序的策略,使开发者能够有能力可以根据具体情况和需求调度任务的执行顺序。

其实无论是单线程还是多线程,平行执行等一些编程优化策略,其目标、意义都是相同的,都是寻求使任务的执行更符合需求,或者更加有效率地使用线程。

示例:

  • 顺序执行。

    let t_finished = 0
    async function randomDelay(id) {
    	const delay = Math.random() * 1000
    	return new Promise(
    		(resolve) => {
    			const t = Date.now()
    			setTimeout(
    				() => {
    					const t_consumed = Date.now() - t
    					console.log(`finished ${id} consumed time: ${t_consumed}`)
    					t_finished += t_consumed
    					resolve(id)
    				},
    				delay
    			)
    		}
    	)
    }  // 创建一个随机时间后落定为解决的期约,模拟复杂耗时的计算任务。
    
    async function sequentialExecutor() {
    	const t = Date.now()
    	for(let i = 0; i < 4; i++) {
    		const t_01 = Date.now()
    		const id = await randomDelay(i)  // 等待开始求值(等待执行计算)
    		const t_02 = Date.now()
    		console.log(`got ${id} awaited time:`, t_02 - t_01)
    	}
    	const t_awaited = Date.now() - t
    	console.log(`all finished consumed time:`, t_finished)
    	console.log(`all awaited time:`, t_awaited)
    }
    
    parallelExecutor()
    
    // 可能的输出:
    // finished 0 consumed time: 391
    // got 0 awaited time: 391
    // finished 1 consumed time: 355
    // got 1 awaited time: 355
    // finished 2 consumed time: 484
    // got 2 awaited time: 484
    // finished 3 consumed time: 656
    // got 3 awaited time: 656
    // all finished consumed time: 1886
    // all awaited time: 1887
    
  • 平行执行。

    let t_finished = 0
    async function randomDelay(id) {
    	const delay = Math.random() * 1000
    	return new Promise(
    		(resolve) => {
    			const t = Date.now()
    			setTimeout(
    				() => {
    					const t_consumed = Date.now() - t
    					console.log(`finished ${id} consumed time: ${t_consumed}`)
    					t_finished += t_consumed
    					resolve(id)
    				},
    				delay
    			)
    		}
    	)
    }
    
    async function parallelExecutor() {
    	const promises = new Array(4).fill(null).map(
    		(_, i) => randomDelay(i)  // 在开始等待之前,开始异步求值(开始执行计算)
    	)
    	const t = Date.now()
    	for(const p of promises) {
    		const t_01 = Date.now()
    		const id = await p  // 等待完成求值(等待完成计算)
    		const t_02 = Date.now()
    		console.log(`got ${id} awaited time:`, t_02 - t_01)
    	}
    	const t_awaited = Date.now() - t
    	console.log(`all finished consumed time:`, t_finished)
    	console.log(`all awaited time:`, t_awaited)
    }
    
    parallelExecutor()
    
    // 可能的输出:
    // finished 1 consumed time: 43
    // finished 2 consumed time: 355
    // finished 3 consumed time: 606
    // finished 0 consumed time: 894
    // got 0 awaited time: 894
    // got 1 awaited time: 0
    // got 2 awaited time: 0
    // got 3 awaited time: 0
    // all finished consumed time: 1898
    // all awaited time: 894
    
    // 补充说明:
    // 类型 Array
    // --原型方法 fill()
    // ----第一个参数:
    // ------任意值,用于替换数组的每个元素的值。
    // ----返回值:
    // ------数组,填充后的数组。
    // --原型方法 map()
    // ----第一个参数:
    // ------函数,会对数组的每个元素执行该函数。
    // --------提供第一个参数:当前元素的值。
    // --------提供第二个参数:当前元素的索引值。
    // ----返回值:
    // ------数组,新的数组,不改变原数组。
    

模拟期约连锁

使用异步函数,模拟期约连锁合成期约值的操作。

示例:

  • 使用异步函数,模拟期约连锁合成期约值的操作。
    async function handler_01(str) {
    	return new Promise(
    		(resolve) => {
    			setTimeout(
    				() => {
    					str += ' -> handler_01'
    					console.log('handler_01 consumed 1s')
    					resolve(str)
    				},
    				1000
    			)
    		}
    	)
    }  // 模拟耗时 1s 的计算任务
    async function handler_02(str) {
    	return new Promise(
    		(resolve) => {
    			setTimeout(
    				() => {
    					str += ' -> handler_02'
    					console.log('handler_02 consumed 1s')
    					resolve(str)
    				},
    				1000
    			)
    		}
    	)
    }
    async function handler_03(str) {
    	return new Promise(
    		(resolve) => {
    			setTimeout(
    				() => {
    					str += ' -> handler_03'
    					console.log('handler_03 consumed 1s')
    					resolve(str)
    				},
    				1000
    			)
    		}
    	)
    }
    
    async function compositeValue(value, ...funcs) {
    	const t = Date.now()
    	for(const func of funcs) {
    		value = await func(value)  // 模拟期约连锁合成期约值
    	}
    	console.log(`compositeValue consumed ${Date.now() - t}ms`)
    	return value
    }
    
    async function asyncLog(value_01, value_02) {
    	if(value_02 !== null) {
    		console.log(await value_01, await value_02)
    	}
    	else {
    		console.log(await value_01)
    	}
    }  // 异步打印
    
    const result = compositeValue('value', handler_01, handler_02, handler_03)
    asyncLog('result:', result)
    
    // 输出:
    // handler_01 consumed 1s
    // handler_02 consumed 1s
    // handler_03 consumed 1s
    // compositeValue consumed 3003ms
    // result: value -> handler_01 -> handler_02 -> handler_03
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值