JavaScript回调函数&Promise总结「上」
1. 同步&异步
想知道回调函数是什么,就要先知道异步和同步。
异步 :async【Asynchronous】
同步 :sync【Synchronous】
这篇文章不解释这个概念,先写回调函数。通俗的比喻一下。
同步就是接力赛,前面没跑完拿到交接棒,你就不能动。按照顺序执行。
异步就是百米冲刺,大家一个起跑线,一起跑。不按照顺序执行。
先下个结论吧。
函数内部异步操作的结果需要回调函数
2. 异步和同步的区别
2-1 执行顺序不一样
■ 同步:按照顺序输出。
最常见的普通函数调用顺序。
console.log('before') function add (x, y) { return x + y } add(1, 2) console.log('after') // before // 3 // after
■ 异步:不按照从上到下的顺序。
让我们用一个异步函数来调用一下 setTimeout()
是一个异步函数,可以发现,并不是按照顺序进行输出。
console.log('before') setTimeout(function(){ console.log('async') },0) //这里写0只是为了证明和定时器的延时无关,只要是异步函数就行。 console.log('after') // before // after // async
那如果全部都是异步函数呢。
这三个函数的结果虽然出来的是1,2,3,但实际上是同时进行的,并非每次出来的都是1,2,3.在对大的文件操作的时候可以验证。
// 这部分代码的输出不是按照1,2,3的顺序,异步操作顺序并非从上至下。 setTimeout(function(){ console.log('1 async') },100) setTimeout(function(){ console.log('2 async') },100) setTimeout(function(){ console.log('3 async') },100)
2-2 结果是否能拿到
■ 同步取得到返回值,异步取不到返回值。
代码上来
// 同步 function add(x,y){ return x + y } const result = add(1,2) //3 // 异步 function getResult(x,y) { setTimeout(function(){ return x + y },2000) } const result = getResult(1,2) //undefined
setTimeout()这个定时器根本不会按照顺序来执行。
在你执行 result = x + y 之前,result 结果就已经 return 出来了。
所以结果是undefined,根本就不是那个结果。
那怎么才能拿到这个return出来的返回值呢。这个时候来了一个回调函数!!
回调函数用来解决2大问题。
- 返回值拿不到的问题 →使用回调函数callback来拿返回值
- 顺序不一致的问题 →使用回调函数也能解决,会产生回调函数地狱的问题。这个问题下篇才来讲。
3. 回调函数登场
3-1 用法
回调函数作为别的函数的参数。把函数作为参数传递给其他函数里面。callback 作为形参,随便取名。不一定是 callback,叫个 cb 也行,但是为了好理解,就统一一下为 callback。
下面是一个函数作为参数回调的案例.
定时器函数 setTimeout()
作为一个异步函数,那么想要他的返回值就必须使用回调函数。
回调函数写法就是在参数上加上一个形参 callback,然后在异步函数内调用这个 callback,把你想要的返回值写在这个函数里面。
那么这个 callback (括号里面写的就是你想要的返回值)
function addAsync(x, y,callback) { setTimeout(function(){ var result = x + y callback(result) // 这一步很重要! // 相当于 var callback = function(ret){console.log(ret)} // 箭头函数写法 const callback = (ret)=>{console.log(ret)} },2000) } addAsync(1,2, function(ret){ // 在这里拿到上面addAsync()的返回值,想干嘛干嘛,这也是封装函数的目的之一。 console.log(ret) //3 })
3-2 简单应用
补充一些知识。
Q:什么函数是回调函数啊
A :一般异步API都需要有一个回调函数,异步操作需要的情况比较多。
比如。setTimeout(),readFile(),writeFile(),ajax()
这些异步操作都需要回调函数,其实常用的JQ里面就全都是回调函数。
const ret = fn() $.get('data.json',function(){ }) // 一般回调函数都是这样来调用的 // 不能这样写很少 const ret = $.get()
封装一个 Ajax 方法,利用回调函数。
// 尝试封装这一段Ajax的get()方法 var oReq = new XMLHttpRequest() oReq.onload = function(){ console.log(oReq.responseText) } oReq.open("get", "data.json", true) oReq.send() // 因为需要得到oReq.responseText这个结果 // 这个结果就在异步函数里,所以用到了回调函数 function get(url,callback){ var oReq = new XMLHttpRequest() oReq.onload = function(){ callback(oReq.responseText) } oReq.open("get", url, true) oReq.send() } get('data.json',function(data){ console.log(data) })
总结
这篇文章只回答了2个问题。
Q :为什么要用回调函数?
A :因为异步操作的结果需要回调函数。
Q :回调函数怎么写?
A :就上面写的。但是还遗留了很多问题,比如异步和同步不同的地方在于2点。
顺序和结果。结果的问题解决了,那如何按照顺序呢。
鉴于篇幅和理解,我分了上下两篇文章。
共有评论(2)
setTimeout(function(){
console.log('1 async')
},100)
setTimeout(function(){
console.log('2 async')
},100)
setTimeout(function(){
console.log('3 async')
},100)
这里的三个异步宏任务一定会按照队列先进先执行的顺序吧,因为代码从上到下执行,看到settiimeout将它的回调函数扔进去宏任务队列
zengzhiyao:
你好,我又仔细看了一下。可以参考我这篇文章。
setTimeout 本身也是同步代码,里面的函数才是宏任务的。
setTimeout(()=>{})里面的callback不保证顺序。
https://chihokyo.com/post/37/
hokyo: