JS中的Promise对象,解决回调地狱案例详解,让代码同步执行

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标识的函数
上一篇:#难点总结


下一篇:斗胆谈谈Promise