一、异步和同步
1、能直接拿到结果的是同步
- 如,在医院挂号,拿到号才会离开
2、不能直接拿到结果的是异步
2.1、举例理解
- 如,在餐厅门口等位,拿到号后可以去逛街。那什么时候能吃到饭?
- 可以每十分钟问一下前面还有几位(轮询)
- 微信扫码接收通知(回调)
2.2、以AJAX为例
request.send()并不能直接得到response- 须等到readyState为4,浏览器会回头调用
request.onreadystatechange函数(类似于每隔几位微信就会发送等位提醒)
2.3、回调callback
- 写给别人用的函数,就是回调
request.onreadystatechange就是我写给浏览器调用的(回头调用一下这个函数,“回头”也有“将来”的意思)- 举例
function f1(){}
function f2(fn){
fn()
}
f2(f1) //f1传给f2(别人),f2调用了f1
//所以f1是回调
2.4、异步和回调
2.4.1、关系
- 合作关系:异步任务需要在得到结果时通知JS来拿结果,一般用回调
- 如何通知,参考2.2的AJAX例子
- 让JS留一个函数地址给浏览器,
request.onreadystatechange - 异步完成后,浏览器会回头调用该函数,即
request.send()后readyState为4时 - 同时把结果作为参数传给该函数(readyState的状态码)
- 这个函数是我写给浏览器调用的,所以是回调函数
2.4.2、区别
- 异步一般用回调通知结果,但不是一定要用回调,也可以用轮询
- 且回调函数也不一定只能用在异步任务里
- 也可用在同步任务里,如
array.forEach(n=>console.log(n)),同步回调
3、如何判断同步/异步
3.1、如果一个函数的返回值处于以下几种
- setTimeout
- AJAX(即XMLHttpRequest),需注意,千万不可把AJAX设置为同步
- AddEventListener
- 那么,就是异步任务
3.2、举例
3.2.1、摇骰子
function 摇骰子(){ //摇骰子()没有写return,其实是return undefined
setTimeout(()=>{ //这个箭头函数有return,返回真正的结果1-6
return parseInt(Math.random()*6)+1
},1000)
}
//这是一个异步任务
3.2.2、摇骰子续
- 如果此时,
const n=摇骰子();console.log(n) //undefined,因为没有拿到结果 - 如何拿到?用回调
function 摇骰子(fn){
setTimeout(()=>{
fn(parseInt(Math.random()*6)+1)
},1000)
}
4、异步有成功/失败结果
4.1、方法一:回调接受两个参数
fs.readFile('/1.txt',(error,data)=>{
if(error){console.log('失败');return}
console.log(data.toString()) //成功
})
4.2、方法二:弄两个回调
ajax('get','/1.json',data=>{},error=>{}) //分别是成功回调和失败回调
ajax('get','/1.json',{
success:()=>{},fail:()=>{} //接受一个对象,有两个key,表示成功和失败
})
4.3、以上方法一二都有问题
- 不规范,名称可以五花八门,success+error、success+fail等
- 很难进行错误处理
- 容易出现回调地狱
(截图来自饥人谷课件)
二、Promise
1、简介
- 1976年,Daniel P.Friedman和David wise提出Promise思想
- 后人基于此发明了Future、Delay、Deferrde等
- 前端结合Promise和JS制定了Promise/A+规范
2、以AJAX封装为例
(截图来自饥人谷课件)
3、改用Promise写法
return new Promise((resolve,reject)=>{...})- ajax()返回了一个含有.then()方法的对象
(截图来自饥人谷课件)
4、总结
- 第一步
return new Promise((resolve,reject)=>{...})- 任务完成则调用resolve(result)
- 任务失败则调用reject(error)
- resolve和reject会再去调用成功和失败函数
- 第二步 1.使用.then(success,fail)传入成功和失败函数