谈起异步编程,我们先来了解下同步执行…如下
同步执行:同步执行不是代码同步执行,而是代码依次执行,后一个任务必须等前一个任务执行完后再执行。执行方式就是代码写的顺序。
同步请求,提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事。
console.log("打印console")
function f1(){
console.log("f1函数")
}
function f2(){
console.log("f2函数")
f1()
}
f2()
f1()
//输出结果:打印console,f2函数,f1函数,f1函数
异步执行:不会等待当前任务执行结束后才会执行下一个任务,当前任务开启后就会往下执行。谁先执行完算谁的
异步请求请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕
console.log("start demo3") //执行1
setTimeout(() => {
console.log("5秒后执行") //执行5
}, 5000);
setTimeout(() => {
console.log("3秒后执行") //执行3
setTimeout(() => {
console.log("1秒后执行") //执行4
}, 1000);
}, 3000);
console.log("end demo3") //执行2
//输出结果:start demo3,end demo3,3秒后执行,1秒后执行,5秒后执行
有兴趣的可以看看这个执行流程图:

一:回调函数
一个函数作为参数传递给另外一个函数(另一个函数我们称为:otherFunction),回调函数就在otherFunction中调用
最简单的demo:
定时器里的匿名函数function就是一个回调函数。(js中的函数是一等公民,所以可以像其他变量一样作为参数进行传递)
setTimeout(function(){
console.log('time out')
},1000)
这样看来,用回调函数处理异步也挺好的,但是需求复杂时,(例子:某个业务依赖于上层业务的数据,上层业务又依赖于更上一层的数据)整个代码会充满了嵌套,读起来麻烦,也不易于扩展。俗称为“回调地狱”
$.ajax({
type: 'get',
url: '/api/getKey',
success: function (data) {
key = data;
$.ajax({
type: 'get',
url: '/api/getToken',
data: {
key: key
},
success: function (data) {
token = data.token;
userId = data.userId;
$.ajax({
type: 'get',
url: '/api/getData',
data: {
token: token,
userId: userId
},
success: function (data) {
console.log('业务数据:', data);
},
error: function (err) {
console.log(err);
}
});
},
error: function (err) {
console.log(err);
}
});
},
error: function (err) {
console.log(err);
}
});
综上:回调函数处理异步的2个问题:
- 1.缺乏顺序性,回调地狱导致 调试困难
- 2.缺乏可信任性,控制反转导致的一系列信任问题
二:Promise
promise解决回调函数处理异步的问题
上面代码用Promise优化为:
//获取key值
let getKeyPromise = function () {
return new Promise(function (resolve , reject){
$.ajax({
type: 'get',
url: '/api/getKey',
success: function (data) {
let key = data;
resolve(key);
},
error: function (err) {
reject(err);
}
});
});
};
//获取token,参数传递key值
let getTokenPromise = function (key) {
return new Promsie(function (resolve, reject) {
$.ajax({
type: 'get',
url: '/api/getToken',
data: {
key: key
},
success: function (data) {
resolve(data);
},
error: function (err) {
reject(err);
}
});
});
};
// 请求业务数据,参数传递key和token
let getDataPromise = function (data) {
let token = data.token;
let userId = data.userId;
return new Promsie(function (resolve, reject) {
$.ajax({
type: 'get',
url: '/api/getData',
data: {
token: token,
userId: userId
},
success: function (data) {
resolve(data);
},
error: function (err) {
reject(err);
}
});
});
};
调用getKeyPromise()方法,依次then调用下面方法
getKeyPromise()
.then(function (key) {
return getTokenPromise(key);
})
.then(function (data) {
return getDataPromise(data);
})
.then(function (data) {
console.log('业务数据:', data);
})
.catch(function (err) {
console.log(err);
});
- Promise对象的then方法会返回一个全新的Promise对象
- 后面的then方法就是在为上一个then返回的Promise注册对应的回调
- 前面then方法中回调函数的返回值会作为后面then方法回调的参数
- 如果回调中返回的是Promise,那后面then方法的回调会等待它的结束
function ajax(url){
return new Promise(function(resolve,reject){
const xhr = new XMLHttpRequest()
xhr.open('GET',url)
xhr.responseType='json'
xhr.onload = function(){
console.log(this.status)
if(this.status == 200){
console.log("请求成功")
resolve(this.response)
}else{
console.log("请求失败")
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
ajax('/api/wyy.json').then(function(value){
console.log(111)
console.log(value)
return ajax('wyy/w1.json')
}) // => Promise {}
.then(function(value){
console.log(222)
console.log(value)
return ajax('wyy/w2.json')
}) // => Promise {}
.then(function(value){
console.log(333)
console.log(value)
return ajax('wyy/w3.json')
})
执行结果:

1. Promise对象有三种状态:
- pending:等待中/进行中,表示还没有得到结果
- resolved( fulfilled ):已经完成,表示得到了我们想要的结果,可以继续往下执行
- rejected:也表示得到了结果,但是结果不是我们想要的,不会往下执行或者执行catch
在Promise对象的构造函数中,将一个函数作为第一个参数,而这个函数,就是用来处理Promise的状态变化的。
new Promise(function(resolve,reject){
if(true){console.log('成功');resolve()}
if(false){console.log('失败');reject()}
})
resolve,reject都为一个函数,作用分别是将状态修改为resolved和rejected。
2. Promise中的数据传递:
var fn = function(num) {
return new Promise(function(resolve, reject) {
if (typeof num == 'number') {
resolve(num);
} else {
reject('TypeError');
}
})
}
fn(2).then(function(num) {
console.log('first: ' + num);
return num + 1;
})
.then(function(num) {
console.log('second: ' + num);
return num + 1;
})
.then(function(num) {
console.log('third: ' + num);
return num + 1;
});
//打印结果:first: 2 second: 3 third: 4
现在所有的库几乎都将ajax请求利用Promise进行封装了。下面简单封装个小demo,以供参考:
// 封装一个get请求的方法
function getJSON(url) {
return new Promise(function(resolve, reject) {
var XHR = new XMLHttpRequest();
XHR.open('GET', url, true);
XHR.send();
XHR.onreadystatechange = function() {
if (XHR.readyState == 4) {
if (XHR.status == 200) {
try {
var response = JSON.parse(XHR.responseText);
resolve(response);
} catch (e) {
reject(e);
}
} else {
console.log("错误打印:")
reject(new Error(XHR.statusText));
}
}
}
})
}
var url = 'https://api/wyy_stydy.com';
getJSON(url).then(res => console.log(res));
3. Promise.all
当一个ajax请求,它的参数需要多个请求返回的结果之后才能确定,那么就需要用Promise.all了
Promise.all接收一个promise对象组成的数组为参数,当这个数组所有的promise对象状态都编程resolved或则rejected时,才会调用then方法
var promiseAll = Promise.all([ // Promise.all() 等待所有任务结束
ajax('/api/dudu1.json'),
ajax('/api/dudu2.json'),
ajax('/api/dudu3.json'),
])
promiseAll.then(function(value){
console.log(value) //等待所有任务结束后才会执行
}).catch(function(error){
console.log(error) //等待所有任务结束后才会执行
})
let urlsArr = {https1:'/api/wei1.json',https2:'/api/wei2.json',https3:'/api/wei3.json'}
ajax(urlsArr)
.then(value => {
const urls = Object.values(urlsArr)
console.log('urls:' , urls)
//打印:["/api/wei1.json", "/api/wei2.json", "/api/wei3.json"]
const task = urls.map(url => ajax(url))
return Promise.all(task)
}).then(values => {
console.log(values)
})
3. Promise.race
与Promise.all类似,都是以一个Promise对象组成的数组作为参数;不同的是当一个Promise对象返回rejected或者resolved时,就会执行.then方法了。
4. Promise捕获异常
catch捕获异常:通过 catch 方式给整个 promise 链条注册失败回调,promise 具有传递性
then方法指定失败回调函数:通过 then 方法的第二个参数指定失败的回调函数,它只能够捕获到前一个 promise 的异常,会出现无法捕获 promise 异常的错误
promise.then((res)=>{
console.log('then' , res)
}).catch((err)=>{
console.log('catch:' , err) //所有then异常都能捕获
})
promise.then((res) => {
console.log('then:' , res)
},(err)=>{
console.log('error:',err) //只能捕获上一个then的异常
})
未完待续... 后面还会持续更新一些Promise使用场景...
本文详细探讨了JavaScript中的异步编程,从同步执行的概念入手,介绍了回调函数及其导致的‘回调地狱’问题。接着,文章重点讨论了Promise如何解决这些问题,包括Promise的三种状态、数据传递、Promise.all、Promise.race以及异常处理机制。通过对Promise的深入解析,帮助读者更好地理解和应用异步编程。
707

被折叠的 条评论
为什么被折叠?



