异步函数的应用
主要参考资料:
- 《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