回调
什么是回调???什么是回调???
被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。
- 请看下面这段代码
- 当执行完打印第一次执行时,再执行一次delay()
函数.然后套娃
- 函数需要一步一步的执行,打印完第一次执行,才能打印第二次执行
- 上面MDN的函数真难看懂,用人话解释一下,回调函数是一个函数,将会在另一个函数完成执行后立即执行。回调函数是一个作为参数传给另一个 JavaScript 函数的函数。这个回调函数会在传给的函数内部执行。
- 可以看到如果这么用回调,括号太多了,可读性属实拉胯.
const delay = (time,callback)=>{
setTimeout(()=>{
callback()},time)
}
delay(1000,()=>{
console.log('第一次执行')
delay(1000,()=>{
console.log('第二次执行')
delay(1000,()=>{
console.log('第三次执行')
delay(1000,()=>{
console.log('第四次执行')
})
})
})
})
- 再举个例子
- 获得ip > 获得城市 > 获得建议
- 括号太多了,数量一多更麻烦,一直嵌套
- 那么有什么方法可以让代码更简洁更直观呢???
getCityFromIp(ip,city => {
getWeatherFromCity(city,weather =>{
getSuggestion(weather,suggestion=>{})
})
})
Javascript 回调是异步的吗?
-
JavaScript 被认为是单线程脚本语言。单线程是指 JavaScript 一次执行一个代码块。当 - JavaScript 忙于执行一个块时,它不可能移到下一个块。
-
换句话说,我们可以认为 JavaScript 代码本质上总是阻塞的。但是这种阻塞性使我们无法在某些情况下编写代码,因为在这些情况下我们没有办法在执行某些特定任务后立即得到结果。
我谈论的任务包括以下情况:
- 通过对某些端点进行 API 调用来获取数据。
- 通过发送网络请求从远程服务器获取一些资源(例如,文本文件、图像文件、二进制文件等)。
为了处理这些情况,必须编写异步代码,而回调函数是处理这些情况的一种方法。所以从本质上上说,回调函数是异步的。
Promise
优点
- 减少缩进
可以把函数嵌套变成.then().then()
f1(a,b =>{
f2(b,c =>{
f3(c)
})
})
//使用promise就是
f1(a)
.then(b =>f2(b))
.then(c =>f3(c))
- 方便错误处理
f1(a)
.then(b =>f2(b),onerror1)
.then(c =>f3(c),onerror2)
.catch(err =>{})
实际举例
通过接口获取IP地址
function getIP(){
let promise = new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest()
xhr.open('GET','http://rap2.taobao.org:38080/app/mock/245421/getIp',true)
xhr.onload = function(){
if (xhr.status === 200){
resolve(JSON.parse(xhr.responseText).ip)
}else{reject ('获取IP地址出现问题')}
}
xhr.onerror = function(){
reject ('获取IP失败')
}
xhr.send()
})
return promise
}
getIP()
.then(function(ip){
console.log(ip)
})//resolve的函数
.catch(function(e){
console.log(e)
})//reject的函数
第二个例子
通过IP获得城市,then()中还可以添加一个promise对象
把then()
中的参数传递进去
function getIP(){
let promise = new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest()
xhr.open('GET','http://rap2.taobao.org:38080/app/mock/245421/getIp',true)
xhr.onload = function(){
if (xhr.status === 200){
resolve(JSON.parse(xhr.responseText).ip)
}else{alert('获取IP地址出现问题')}
}
xhr.onerror = function(){
reject ('获取IP失败')
}
xhr.send()
})
return promise
}
const getCityFromIp = ip => {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET',
'http://rap2.taobao.org:38080/app/mock/245421/getCity?ip='+ip, true)
xhr.onload = () => {
resolve(JSON.parse(xhr.responseText).city)
}
xhr.onerror = () => reject('获取city失败')
xhr.send()
})
}
const getWeatherFromCity = city => new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET',
'http://rap2.taobao.org:38080/app/mock/245421/getWeather?city='+city, true)
xhr.onload = () => {
if(xhr.status === 200) {
resolve(JSON.parse(xhr.responseText))
} else {
reject('天气接口异常')
}
}
xhr.onerror = () => reject('获取天气失败')
xhr.send()
})
getIP()//下面得到ip
.then( ip =>getCityFromIp(ip))//得到ip参数,传入getCityFromIp()函数,下面得到city
.then(city => getWeatherFromCity(city))//得到city参数传入getWeatherFromCity(),下面得出weather
.then(weather => console.log(weather))//最终得到weather,展示出
.catch(function(e){
console.log(e)
})
API
Promise是一个类
类是一个特殊的函数
-
类方法:(函数里的属性)
- Promise.all
- Promise.race
- Promise.reject
- Promise.resolve
-
对象属性:(构造函数原型上的属性)
- Promise.peototype.then
- Promise.peototype.finally
- Promise.peototype.catch
-
对象内部属性
- state = pedding/fullfilled/rejected
Promise/A+ 规范文档
Promise.race
因为异步加载不是按顺序加载的,因此发送对个ajax的请求是根据网速来决定加载快慢,那么这个时候怎么决定谁先达到结果呢?
- Promise.race,得到一个最先resolve的结果
const getWeather = city => new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET', 'http://rap2.taobao.org:38080/app/mock/245421/getWeather?city='+city,
true)
xhr.onload = () => resolve(JSON.parse(xhr.responseText))
xhr.send()
})
let p1 = getWeather ('北京')
let p2 = getWeather ('上海')
let p3 = getWeather ('杭州')
//下面括号里是个数组
Promise.race([p1, p2, p3]).then(data => {
console.log(data)
})
Promise.all
把全部的resolve的结果放进一个数组
- 只有等全部数据加载完了,才会返回结果哦
const getWeather = city => new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET', 'http://rap2.taobao.org:38080/app/mock/245421/getWeather?city='+city,
true)
xhr.onload = () => resolve(JSON.parse(xhr.responseText))
xhr.send()
})
let p1 = getWeather ('北京')
let p2 = getWeather ('上海')
let p3 = getWeather ('杭州')
Promise.all([p1, p2, p3]).then(data => {
console.log(data)
})
async/await
使用方法
就是个语法糖
- await需要在Promise对象之前
- await只在async函数内有效
- 缺点是他的加载顺序是串联的,即 a > b > c,有时候得使用promise.all
const getWeather = city => new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET', 'http://rap2.taobao.org:38080/app/mock/245421/getWeather?city='+city,
true)
xhr.onload = () => resolve(JSON.parse(xhr.responseText))
xhr.send()
})
async function start(){
let w1 = await getWeather('北京')
console.log('北京:'+w1.weather)
let w2 = await getWeather('上海')
console.log('上海:'+w2.weather)
}
start().catch(err => console.log(err))
//等同于下面的promise写法
function start2(){
getWeather('北京')
.then(w1 =>{
console.log('北京'+w1.weather)
})
.then(() =>getWeather('上海'))
.then(w2 =>{
console.log('上海'+w2.weather)})
}
start2()