什么是异步什么是同步
如果能直接拿到结果,就是同步
-
比如医院挂号,拿到号才会离开窗口
-
不拿到结果不会离开
如果不能直接拿到结果,就是异步
-
比如用餐排队拿号,拿到号以后可以干别的,号到你了才回来用餐
-
可以每十分钟去问一下(轮询)
-
也可以扫码用微信接收通知(回调)
异步举例
以AJAX为例
-
request.send()之后,并不能直接得到response -
必须等到
readyState值为4后,浏览器才回头调用request.onreadyStateChange -
才能得到
request.response -
跟餐厅给你发消息提醒你该你用餐了是一样的
回调 call back
-
写给自己用的函数不是回调,写给别人用的函数,就是回调
-
request.onreadyStateChange就是我写给浏览器调用的 -
中文里,回头也有将来的意思,比如我回头请你吃饭,表示以后请你吃饭
-
写了却不调用,给别人调用的函数,就是回调
回调举例1
// 把函数1给另一个函数2
function f1() {}
function f2(fn) {
fn()
}
f2(f1)// 没有直接调用f1,而是把f1传给f2调用,f1就是回调
回调举例2
function f1(x) {
console.log(x);
}
function f2(fn) {
fn('你好')
}
f2(f1)// x可以改成任意名字,x表示第一个参数而已
异步与回调的关系
关联
-
异步任务需要在得到结果时通知JS来拿结果,问题是怎么通知
-
可以让JS留一个函数地址(用餐时留下的电话号码)给浏览器
-
异步任务完成时,浏览器用该函数地址即可(排到你时餐厅拨打你的电话通知你)
-
同时把结果作为参数传给该函数
-
这个函数是我写给浏览器调用的,所以是回调函数
区别
-
异步任务需要用到回调函数来通知结果
-
但回调函数不一定只用在异步任务里
-
回调还可以用到同步任务里
-
比如
array.forEach( n => console.log(n))就是同步回调
判断同步异步
如果一个函数的返回值处于
-
setTimeout -
AJAX(即XMLHttpRequest) -
AddEventListener -
这三个东西内部,那么这个函数就异步函数
需要注意
-
网上说AJAX可以设置为同步的,但只有傻*前端才会这么做
-
因为如果设置同步的,那么在响应的时候,整个页面会停下来等待响应
摇骰子函数举例
function 摇骰子() {
setTimeout(() => {
return parseInt(Math.random() * 6) + 1
}, 1000)
// return undefined
}
分析
-
摇骰子()没有写return,那就是return的undefined -
箭头函数有
return,返回真正的结果 -
所以这是一个异步函数/异步任务
摇骰子函数怎么拿到异步结果
const n = 摇骰子()
console.log(n); // undefined
用回调
function f1(x) {
console.log(x)
}
摇骰子(f1)
要求摇骰子函数得到结果后把结果作为参数传给f1
function 摇骰子(fn) {
setTimeout(() => {
fn(parseInt(Math.random() * 6) + 1)
}, 1000)
}
// 简化为箭头函数 由于 f1 声明后只用了一次,所以可以删掉 f1
function f1(x) {
console.log(x);
摇骰子(f1)
// 改成
摇骰子(x => {
console.log(x);
})
// 还能简化
摇骰子(console.log)
// 如果参数个数不一致就不能这样简化
总结
-
异步任务不能拿到结果
-
于是我们传一个回调给异步任务
-
异步任务完成时调用回调
-
调用的时候把结果作为参数
如果异步任务有两个结果:成功或失败怎么办?
方法1:回调接受两个参数 node.js就这么做的
fs.readFile('./1.txt', (error, data) => {
if (error) {
console.log('失败');
return
}
console.log(data.toString()); //成功
})
方法2:搞两个回调
// 可以这样
ajax('get', '/1.json', data(() => {}), error(() => {}))
// 前面函数是成功回调,后面函数是失败回调
// 还可以这样
ajax('get', '/1.json', {
success: () => {},
fail: () => {}
}) // 接受一个对象,对象有两个Key 表示成功和失败
这些方法的不足(为什么要用Promise)
-
不规范,命名五花八门,每个程序员使用的名称可能都不一样
-
容易出现回调地狱,代码变得看不懂,比如下面代码这样
getUser(user => {
getGroups(user, (groups) => {
groups.forEach((g) => {
g.filter(x => x.ownerId === user.id).forEach(x => console.log(x))
})
})
})
- 或这样
- 还很难进行错误处理
解决方法
- 用Promise
Promise
-
为了解决上述三个问题,前端程序员找到了Promise思想
-
1976年,丹尼尔和大卫提出的Promise
-
后人基于这个思想发明了Future、Delay、Deferred等
-
前端结合Promise和JS制定了 Primise/A+规范
以AXAJ封装为例
- 传统封装
ajax = (method, url, options) => {
const {
success,
fail
} = options // 析构赋值
// const success = options.success
// const fail = options.fail
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 是 function 缩写 ,右边是箭头函数
// 用到了两个回调,还是使用了 success 和 fail
Promise说这代码太傻了,所以我们改成Promise的写法
//封装
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()
})
}
//调用
ajax('get', '/xxx').then((response) => {}, (request) => {})
-
虽然也是回调,但是不需要记 success 和 fail 了
-
then 的第一个参数就是 success,then 的第二个参数就是 fail
-
ajax()返回了一个含有 .then() 方法的对象
学习Promise
-
背五个单词
-
return new Promise ((resolve , reject) => {}) -
用熟练它们
总结
第一步
-
return new Promise ((resolve , reject) => {}) -
任务成功则调用
resolve(result) -
任务失败则调用
reject(error) -
resolve和reject会再去调用成功和失败函数
第二步
- 使用
.then(success,fail)传入成功和失败函数
第三步
- 以后学习到promise更高级的用法时再补充
axios
刚才封装的ajax有很多缺点
-
post无法上传数据
request.send(这里可以上传数据) -
不能设置请求头
request.setRequestHeader(key,value)
怎么解决
-
花时间把
ajax写到完美(主要现在没这么多时间) -
使用
jQuery.ajax(过时的API,但可以学习使用,看看文档) -
使用
axios(这个库是目前主流的库)
axios介绍
-
目前最新的AJAX库
-
抄袭了 jQuery 的封装思路
-
通过大佬的博客Axios 作弊表可以快速了解axios的用法
代码示例
axios.get('/5.json')
.then(response =>
console.log(response)
)