一、什么是同步和异步?
- 同步:直接能拿到结果
- 异步:不能直接拿到结果
在JS中,有的函数不能立刻拿到结果,需要等一会才能拿到结果,比如发送HTTP请求,等待响应需要一段时间。
如果 JS 不能直接拿到一个函数的结果,可以先去执行别的代码,等结果到了再取结果,这就是异步
异步的好处是可以把用来等待的时间拿去做别的事情
怎么判断一个函数的结果是同步的还是异步的呢?
如果一个函数的返回值在setTimeout, AJAX, AddEventListener 这三个东西内部,那么这个函数就是异步函数
二、什么是回调?
我们希望能够拿到异步函数的结果,有两种方法:轮询和回调
- 轮询就是我定时去询问拿到结果了没
- 回调就是我写一个函数fn,然后传给异步函数,当异步函数执行完毕时,就会调用fn, 于是我们就通过回调拿到了异步处理的结果
满足以下条件的函数就是回调函数:我写给别人用,自己不用的函数
举例:
- 我写了函数A,传给函数B, B会调用函数A, 那么A就是回调
-
- arr.forEach((item)=>console.log(item))
- (item)=>console.log(item) 这个箭头函数就是一个回调
- 有的时候回调还可以传给一个对象,
-
- 当我们写AJAX请求时,我们把回调传给 request.onreadystatechange,等待浏览器来调用
图中标黄的函数就是回调函数
注意:回调可以用于同步任务,不一定非要用于异步任务
回调举例
function 摇骰子(){
setTimeout(()=>{
return parseInt(Math.random()*6) + 1
},1000)
}
注意在这个代码里,
摇骰子()没有写return ,即 return undefined,
箭头函数里有return, 返回真正的结果,所以这是一个异步函数
const n = 摇骰子()
console.log(n)// n是Undefined
那么我们应该怎么去拿到setTimeout的结果呢?
可以用回调
function callback(x) {
console.log(x)
}
摇骰子(callback){
setTimeout(()=>{
callback(parseInt(Math.random()*6) + 1)
},1000)
}
我们可以把callback函数简化成箭头函数
摇骰子(x=>{
console.log(x)
})
// 由于这里函数的参数和需要用到的值是一样的,所以还可以简化为
摇骰子(console.log)
但是如果参数个数不一样,就不能这么简化,
举例:
const array = ['1', '2', '3'].map(parseInt)
得到的结果是 [1, NaN, NaN]
这里错误的原因就是因为 忽略了Map函数和parseInt函数参数不同
首先我们来复习一下map函数
let new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
map的回调函数有三个参数,分别是currentValue, index, array本身
而parseInt有两个参数string和 radix,表示将一个字符串string 转换为radix 进制的整数
parseInt(string, radix) 将一个字符串string 转换为radix 进制的整数
const array = ['1', '2', '3'].map(parseInt)
这种写法等价于
['1', '2', '3'].map((currentValue, index, array) => {
parseInt(currentValue, index, array)
// parseInt('1', 0) 把'1'换成0进制,那就是1
// parseInt('2', 1) 把'2'换成1进制,1进制里没有2,所以是NaN
// parseInt('3', 2) 把'3'换成2进制,2进制里没有3,所以是NaN
})
下面是正确的写法,就是写明参数即可
['1', '2', '3'].map((currentValue, index, array) => {
parseInt(currentValue)
}
三、总结
我们来总结一下异步与回调的关系
-
异步任务不能拿到结果
-
于是我们传一个回调给异步任务
-
异步任务完成时调用回调函数
-
调用的时候把结果作为参数