同步和异步
- 同步:直接能拿到结果
- 异步:不能直接拿到结果
判断一个函数是同步的还是异步
如果函数处于
- setTimeout
- AJAX
- AddEventListener
这三个内部,就是异步
回调
拿到异步函数的结果,可以通过轮询和回调
- 轮询就是每过一段时间去查看拿到了没
- 回调就是写了却不调用,回头给别人调用的函数
举例
- 把函数1给另一个函数2
function f1(){}
function f2(fn){
fn()
}
f2(f1)
- 我没有调用f1
- f1传给了f2
- f2调用了f1
- f1是我写给f2调用的函数
- 所以f1是回调
- 摇骰子
function (){
setTimeout(()=>{
return parseInt(Math.random() * 6) + 1
},1000)
// return undefined
}
- 摇骰子()没有写return ,所以return undefined
- 箭头函数里有return, 返回真正的结果
- 所以这是一个异步函数
- 如何拿到异步结果
function callback(x) {
console.log(x)
}
摇骰子(callback){
setTimeout(()=>{
callback(parseInt(Math.random()*6) + 1)
},1000)
}
简化为箭头函数
摇骰子(x=>{
console.log(x)
})
// 由于这里函数的参数和需要用到的值是一样的,所以还可以简化为
摇骰子(console.log)
参数个数不一致不能这么简化
const array = ['1', '2', '3'].map(parseInt)
//得到的结果是 [1, NaN, NaN]
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)
}
小结
- 异步任务不能拿到结果
- 于是我们传一个回调给异步任务
- 异步任务完成时调用回调函数
- 调用的时候把结果作为参数
promise
回调地狱
如果异步任务有多个结果,在没有promise时,会出现回调地狱
getUser( user => {
getGroups(user, (groups)=>{
groups.forEach( (g)=>{
g.filter(x => x.ownerId === user.id)
.forEach(x => console.log(x))
})
})
})
而且这种写法还会有另外的问题
- 回调函数命名缺乏规范, 每个程序员都有可能给回调函数起不同的名字
- 不能很好地捕获错误
promise
使用promise的好处
- 解决回调地狱,增强代码可读性
- 规范回调的名字或顺序
- 很方便地捕获错误
以AJAX的封装为例
ajax = (method, url, options)=>{
const {success, fail} = options
const request = new XMLHttpRequest()
request.open(method, url)
request.onreadystatechange = ()=>{
if(request.readyState === 4){
// success fail
if(request.status < 400){
success.call(null, request.response)
}else if(request.status >= 400){
fail.call(null, request, request.status)
}
}
}
request.send()
}
ajax('get', '/xxx', {
success(response){}, fail: (request, status)=>{}
}) //成功执行success,失败执行fail
可改写成
ajax = (method, url, options)=>{
return new Promise((resolve, reject)=>{
const {success, fail} = options
const request = new XMLHttpRequest()
request.open(method, url)
request.onreadystatechange = ()=>{
if(request.readyState === 4){
// 成功resolve,失败 reject
if(request.status < 400){
resolve.call(null, request.response)
}else if(request.status >= 400){
reject.call(null, request)
}
}
}
request.send()
})
}
小结
- 第一步
- return newPromise((resolve,reject)=>{...})
- 任务成功调用resolve(result)
- 任务失败调用reject(error)
- resolve和reject会再去调用成功和失败函数
- 第二步
- 使用.then(success,fail)传入成功和失败函数