往往异步与同步的概念我们模模糊不清
那什么是异步?什么是同步呢?
异步:如果不能直接拿到结果,就是异步。
举例:如果你在餐厅门口等座位,你拿到号可以去逛街。什么时候真正吃饭呢?
你可以每隔10分钟去问一下(轮询)你也可以扫码用微信直接收通知(回调)
以AJAX为例:request.send()之后,并不能直接得到response,console.log(request.respone)试试,必须等到 readyState变为4后,浏览器回头调用request.onreadyStatechage函数,我们才能得到request.response,这和餐厅给你发微信提醒的过程是类似的。
同步:如果能直接拿到结果,就是同步
举例:比如你在医院挂号,你拿到号才离开窗口,同步任务可能消耗10毫秒,也可能消耗3秒,总之,不拿到结果你是不会离开的。
回调(callback):写了却不调用,给别人调用的函数就是回调。
你写给自己用的函数不是回调,你写给别人用的函数是回调。
举例:把函数1给函数2 f1是回调
function f1(){}
function f2(fn){
fn()
}
f2(f1)
分析:
1.我调用f1没有?答:没有调用
2.我把f1传给f2了没有?答: 传了
3.f2调用f1没有?答:调用了
4.那f1是不是我写给f2调用的函数?答:是所以f1是回调
function f1(x){
console.log(x)
}
function f2(fn){
fn('你好’)
}
f2(f1)
- f1怎么会有一个x参数
fn('你好')中的fn就是f1;
fn('你好')中的你好会被赋值给参数x,所以x就是'你好',x可以改成任意其他名字,x表示第一个参数而已
异步和回调的关系
关联
异步任务需要在得到结果时通知JS来拿结果
怎么通知呢?
可以让JS留一个函数地址给浏览器,异步任务完成时,浏览器调用该函数地址即可。同时把结果作为参数传给该函数(电话里通知你可以来吃饭了),这个函数是我写给浏览器调用的,所以是回调函数。
区别
异步任务需要用到回调函数来通知结果,但回调函数不一定只能用在异步任务里。回调可以用到同步任务里array.forEach(n=>console,log(n))就是同步回调
异步为什么会用到回调?
它为了拿到那个不能拿到的结果,必须使用回调。
判断同步异步:
1如果一个函数的返回值处于
- setTimeout
- AJAX(即XMLHttpRequest)
- AddEventListener
这三个东西内部,那么这个函数就是异步函数
AJAX设置为同步,会让请求期间页面卡住,不建议这样做。
2.判断同步异步举例
function 摇骰子(){
setTimeout(()=>{
return parseInt(Math,radom()*6)+1},1000)
return undefined
}
分析:
摇骰子()没有写return,那就是return undefined,箭头函数里有return返回真正的结果,所以这是一个异步函数/异步任务。
return属于不同的函数。
摇骰子续
const n= 摇骰子()
console.log(n)//undefined
怎么才能拿到异步结果?
可以用回调,写个函数,然后把函数地址给它
function f1(x){console.log(x)} 摇骰子 (f1)
简化箭头函数:
由于f1声明之后只用了一次,所以可以删掉f1
function f1(x){console.log(x)} 摇骰子 (f1)
改为
摇骰子(x=>{console.log(x)})
再简化
摇骰子(console.log)
注意:如果参数个数不一致就不能这样简化
回调错误简化案例
const array =['1','2','3'].map(parseInt)
console.log(array)
结果:[1,NaN,NaN]
分析:
直接传给parseint相当与
//parseInt(‘1’,0,arr)=>1
//parseInt(‘2',1,arr)=>NaN
把2当作二进制解析,二进制没有2,就得到NaN,同理3也得到NaN
本来形式
const array=['1','2','3'].map((item,i,arr)=>{
return parseInt(item)
})
console.log(array)
结果:[1,2,3]o
异步任务不能拿到结果,于是我们传一个回调给异步任务,异步任务完成时调用回调,调用的时候把结果作为参数。
思考:如果异步任务有两个结果成功或失败,怎么办?
方法一:回调接受两个参数
方法二:搞两个回调
但是回调时异步回调时存在问题的,容易出现回调地狱
Node
中有大量的异步 IO 操作,被封装成基于回调的函数,遇到复杂的业务逻辑很容易形成多级缩进的代码,在左侧形成一个由空格(或 Tab)组成的三角形,代码变得非常难读,被称为回调地狱。
回调有什么不足?
- 命名不规范
- 容易出现回调地狱,代码变得看不懂
- 很难进行错误处理
怎么解决上述三不足呢?
因此就有了Promise
Promise 是目前前端解决异步问题的统一方案
promise是什么?
是1976年发明的一种设计模式(写的好的代码,取个名字就叫设计模式)
写法:
只需要在函数里直接return;
return new Promise((resolve,reject)=>{})
//任务成功的时候调用 resolve,失败的时候调用 reject
//注意resolve和reject也是函数且只接收一个参数
//resolve 和 reject 可以改成任何其他名字,不影响使用,但一般就叫这两个名字
Promise 介绍:
- 1976年,Danlel P.Fredman和David lai两人提出Promise思想,后人基于此发明了Future,Delay,Defeered等
- Promise 不是前端发明的,前端结合了Promise和JS制定了Promise/A+规范,该规范详细描述了Promise的原理和使用方法
- window.Promise 是一个全局函数,可以用来构造 Promise 对象
如何使用Promise?
第一步
return new Promise((resolve, reject)=> {})
任务成功则调用resolve(result);任务失败则调用reject(error),resolve和reject会再去调用成功和失败函数
第二步
使用.then(Success,fail)传入成功和失败函数
使用 return new Promise((resolve, reject)=> {}) 就可以构造一个 Promise 对象,构造出来的 Promise 对象含有一个 .then() 函数属性
resolve 和 reject 并不是 .then(succes, fail)里面的 success 和 fail,resolve 会去调用 success,reject 会去调用 fail,虽然也是回调,但不需要记success和fail了,then的第一个参数就是success,then的第二个参数就是fail