js中的异步模式--- 回调函数篇
js中的异步 使用像 JavaScript 这样的语言编程时,很重要但常常被误解的一点是,如何表达和控制持续一段时间的程序行为。 如从数据库或文件系统中请求数据然后显示数据、通过网络发送数据并等待响应,或者是在以固定时间间隔执行重复任务(比如动画)等。这些程序并不会以及运行结束,通常一部分运行在当下,另一部分运行在将来。 事实上,程序中现在运行的部分和将来运行的部分之间的关系就是异步编程的核心。 ---------------------<你不知道的Javascript> 回调函数 首先要闹明白两个问题: 问题一:什么是回调函数? 答:函数A中传入函数B作为参数时,函数B即为A函数执行的回调函数。(设计JavaScript中传值调用,函数可作为值传递等知识点) 问题二:回调函数都是异步吗? 由回调函数定义可推导,并非全是异步。 //非异步 const arr = [1,2,3] const cb =item => { console.log(item) } //callback arr.map(cb) //异步 setTimeout(()=>{ console.log('setTimeout')},0) 一段代码开始: //分析如下代码输出 console.log('start') setTimeout( function(){ console.log('setTimeOut') }, 1000 ); console.log('end') // start -> end -> setTimeout 以上代码可抽象出两个问题: 1、代码的执行顺序非线性(定义顺序与执行顺序不一致) 2、回调函数执行强依赖于宿主环境(setTimeout函数不是javascript自带方法,类似有fs.readFile),导致信任问题 对于第一个问题,随着代码量增加,理解代码本身的难度会剧增。如 listen( "click", function handler(evt){ setTimeout( function request(){ ajax( "http://some.url.1", function response(text){ if (text == "hello") { handler(); } else if (text == "world") { request(); } }); }, 500) ; });
当我们回顾项目,看到这一段代码时,我们的眼球会在各个事件及其对应的回调之间周转,未必能立即理解该代码所表示的含义。 我们大脑中的逻辑为: A、当前,添加click事件监听函数 B、未来(触发click事件), 调用setTimeout C、未来的未来(500ms定时触发),调用request D、最远的未来(ajax请求返回),判断返回值 这时候感慨一声:如果代码能像A、B、C、D一样简洁那该多好! 对于第二个问题,做详细诠释: 1、怎么理解这里说的信任问题? const cb = ()=>{console.log('可信任?')} setTimeout(cb,1000) cb只执行一次吗? 可能这里使用了标准化的setTimeout方法,你会说yes,当然只执行一次。那么,如果你使用的是第三方库呢? 也许可以这样解决 let trust = false const cb = ()=>{ if(!trust){trust = true} console.log('可信任?') } setTimeout(cb,1000) 那么, • 调用cb过早(在追踪之前); • 调用cb过晚(或没有调用); • 调用cb的次数太少或太多; • 没有把所需的环境 / 参数成功传给你的回调函数; • 吞掉可能出现的错误或异常; 呢? 2、如何解决信任问题? 信任问题产生的根源在于,虽然由我们自己定义回调函数,但回调函数的执行却依赖于第三方。 解决这个问题,就必须在javascript中内置异步处理模式,promise粉末登场