...

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点。
顺序和结果。结果的问题解决了,那如何按照顺序呢。
鉴于篇幅和理解,我分了上下两篇文章。

下一篇:回调函数&Promise总结「下」

共有评论(2)

...
zengzhiyao

setTimeout(function(){  

        console.log('1 async')  

    },100)  

setTimeout(function(){  

        console.log('2 async')  

    },100)  

setTimeout(function(){  

        console.log('3 async')  

    },100)  

这里的三个异步宏任务一定会按照队列先进先执行的顺序吧,因为代码从上到下执行,看到settiimeout将它的回调函数扔进去宏任务队列

2024-11-29
...
hokyo  博主   回复了 zengzhiyao

你好,我又仔细看了一下。可以参考我这篇文章。
setTimeout 本身也是同步代码,里面的函数才是宏任务的。setTimeout(()=>{})里面的callback不保证顺序。
https://chihokyo.com/post/37/

2024-12-02
登陆即可评论哦