Promise对象,解决回调地狱,同步执行代码
此文章记录一下所研究的知识点
BY:林忆
前言:
-
异步操作是JS中的一个优势,同时又带来了极大的麻烦,有一个词叫回调地狱,也就是在一个回调函数里面,执行另外一个异步任务,一直往里面嵌套,导致可读性非常差,不便于维护
-
Promise就是ES6新出的一种解决方案,用来解决回调地狱
-
Promise能让异步代码变成同步执行
-
异步任务有:事件,定时器,Ajax请求,文件操作,数据库操作。异步任务只能由回调函数接收结果
// 回调地狱,案例
// 异步操作什么时候返回结果是不可控的,如果要按顺序来请求,那么就只能将这些操作嵌套起来
$.get(url,function(res){
$.get(url,function(res){
$.get(url,function(res){
console.log(res); // 拿到最终结果
})
})
})
// 下面有 回调地狱 解决案例代码
介绍
-
Promise是异步编程的一种解决方案,从语法上看,它是一个对象,使用时需要new
-
Promise 可以理解为一个容器,里面可以编写异步代码。
-
new Promise 和 new 其它对象一样,是同步任务。
-
获取结果的时候,调用 resolve 触发then方法时 是异步的,成功的结果给resolve,失败给reject
创建Promise对象和使用
// 创建Promise对象
// 其中 传入的函数会自动执行,并且会立即执行,属于同步任务
let p = new Promise(function (resolve, reject) {
$.ajax({
url: 'http://123.57.109.30:3000/api/categoryFirst',
success(res) {
resolve(res); //成功调用 resolve方法
},
error(err) {
reject(err); //失败调用 reject方法
}
})
})
// 调用 方式1
p.then(function (res) {
console.log(res); // 获取成功的结果
}).catch(function (err) {
console.log(err); // 失败的结果
})
// 调用 方式2
p.then(成功回调函数, 失败回调函数);
p.then(function(res){}, function(err){}); //语法
Promise的三种状态
对象的状态不受外界影响,返回的promise对象代表一个异步操作,有三种状态
一旦状态改变,就不会再变,任何时候都可以得到这个结果
// 封装函数,返回Promise对象
function myAjax(url) {
return new Promise(function (resolve, reject) {
$.ajax({ url: url,success(res){ resolve(res) }, error(err){ reject(err) }})
})
}
let p = myAjax("http://xxx.com/api/getbook");
console.log(p); // 得到的Promise对象代表能得到异步操作状态
// Pending(进行中),此时Promise的结果为undefined
// Resolved(已完成,又称Fulfilled),此时Promise的结果为 传递给 resolve 函数的值
// Rejected(已失败)。此时 Promise的结果为 传递给 reject 函数的值
then()方法中,返回非Promise对象值,值直接给下一个then里使用
// then()方法中,返回非Promise对象值,值直接给下一个then里使用
// 如果返回的是Promise对象,则会代替then默认返回的那个Promise对象
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1秒执行完毕')
}, 1000);
})
// then执行后 会得到一个全新的Promise对象,newP存的就是新的
let newP = p.then(res => {
console.log(res); // 执行完毕
return 1212; // 返回给下一个Promise对象使用, 打印 "1秒执行完毕"
})
// 链式调用
newP.then(function (res) {
console.log(res); // 得到上一个Promise对象的返回值, 打印 1212
})
回调地狱方案1
// 如果返回的是Promise对象,则会代替then原地返回的新Promise对象
function myAjax(url) { // 封装函数,返回Promise对象
return new Promise(function (resolve, reject) {
$.ajax({
url: url,
success(res){ resolve(res) },
error(err){ reject(err) }
})
})
} //-----------------------下面的代码也可以省略接收步骤,直接链式写法
// 获取1级分类数据
let p = myAjax('http://123.57.109.30:3000/api/categoryFirst'); // 返回Promise对象
let newP = p.then(function(res){
console.log(res); // 得到1级分类的数据
// 获取2级分类数据,并返回Promise对象
return myAjax('http://123.57.109.30:3000/api/categorySecond?firstId=' + res.list[8].firstId);
})
let newPP = newP.then(function(res){
console.log(res); // 得到2级分类的数据
// 获取3级分类,并返回Promise对象
return myAjax('http://123.57.109.30:3000/api/categoryThird?secondId=' + res.list[2].secondId);
})
newPP.then(function(res){
console.log(res); // 得到3级分类的数据
})
async 和 await
async 和 await 是 ES2017 中提出来的,它们的出现简化了Promise的使用
async 用于修饰一个 function
,修饰的函数总是返回一个Promise对象
await 只能出现在 async 修饰的函数内
,await会等待直到返回结果,否则会一直等待下去,但不会影响函数体外面的代码。
最终解决方案
// async 和 await 使用,最终完美的解决方案
async function getAjax(){
// 获取1级分类数据, myAjax方法和 上面的方案1中的一样
let res = await myAjax('http://123.57.109.30:3000/api/categoryFirst');
// 获取2级分类数据
res = await myAjax('http://123.57.109.30:3000/api/categorySecond?firstId=' + res.list[8].firstId);
// 获取3级分类
res = await myAjax('http://123.57.109.30:3000/api/categoryThird?secondId=' + res.list[2].secondId);
console.log(res); // 得到最终结果
}
getAjax(); // 调用 async标识的函数