Promise

1、准备

1.1、区别实例对象与函数对象

1.实例对象: new函数产生的对象,称为实例对象,简称为对象
2.函数对象:将函数作为对象使用时,简称为函数对象

    function Fn(){//Fn函数

    };
    let fn = new Fn();//Fn是构造函数、fn是实例对象(简称为对象)
    console.log(Fn.prototype);//Fn是函数对象
    Fn.bind({});//Fn是函数对象
    $("#test");//jQuery 函数
    $.get("/test");//jQuery函数对象

1.2、两种类型的回调函数

1.2.1、同步回调

1、理解: 立即执行,完全执行完了才结束,不会放入回调队列中
2、例子: 数组遍历相关的回调函数、Promise的 excutor函数


例一、数组遍历相关的回调函数
let arr = ["a","b","c"];
arr.forEach(r => { //遍历回调函数
    console.log("11111");
});
console.log("222222222");

例二、Promise的 excutor函数
new Promise(resolve => {
    console.log("11111");
})
console.log("222222");

1.2.2、异步回调

1、理解: 不会立即执行,会放入回调队列中将来执行
2、例子: 定时器回调 / ajax回调 / Promise的成功|失败的回调

例一、定时器回调
setTimeout(() =>{
    console.log("11111");
}, 0);
console.log("222222222");

例二、Promise的成功|失败的回调
new Promise(resolve => {
    resolve("")
}).then(res => {
    console.log("1111");
})
console.log("222222");

1.3、JS 的 error 处理

1.3.1、错误的类型

1、Error: 所有错误的父类型
2、ReferenceError: 引用的变量不存在
3、TypeError: 数据类型不正确的错误
4、RangeError: 数据值不在其所允许的范围内
5.、SyntaxError: 语法错误

// 1、Error: 所有错误的父类型
// 2、ReferenceError: 引用的变量不存在
    console.log(a);//ReferenceError: a is not defined
    console.log("----------");//没有捕获 error,下面的代码不会被执行(这行代码没有被执行)

// 3、TypeError: 数据类型不正确的错误
    let b;
    // console.log(b.xxx);//TypeError: Cannot read property 'xxx' of null
    b = {};
    b.xxx();//TypeError: b.xxx is not a function

// 4、RangeError: 数据值不在其所允许的范围内
    function fn(){
        fn();
    };
    fn();//RangeError: Maximum call stack size exceeded

// 5.、SyntaxError: 语法错误
    const c = """";//SyntaxError: Unexpected string

1.3.2、错误处理

1、捕获错误:try ... catch
2、抛出错误:throw error

//1、捕获错误:try ... catch
try{
    let b;
    console.log(b.xxx );
} catch (error){
    console.log(error.message);
    console.log(error.stack);
}
console.log("出错之后,通过捕获错误,程序能继续处理");

//2、抛出错误:throw error
function something(){
    if(Date.now()%2 == 1){
        console.log("当前时间为奇数,可以执行任务");
        something()
    }else{//如果时间是偶数抛出异常,由调用来处理
        throw new Error("当前时间为偶数,无法执行任务");
    }
}
try{
    something()
} catch(error){
    alert(error.message)
}

2、Promise的理解与使用

2.1、Promise是什么?

2.1.1、理解

抽象表达

1、promise 是ES6中提供的一个异步编程的新的解决方案(旧的是谁?)

具体表达

1、从语法上来说: Promise是一个构造函数
2、从功能上来说: promise对象用来封装一个异步操作,并可以获取其结果


2.1.2、Promise 状态与状态的改变

Promise对象有三种状态:

  • 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled): 意味着操作成功完成。
  • 已拒绝(rejected): 意味着操作失败。

这三种状态的变化途径只有2种:
1、pending 变为 resolved
2、pending 变为 rejected
 说明:只有这2种,且一个promise对象只能改变一次
   无论变为成功还是失败,都会有一个结果数据
   成功的结果数据一般称为value,失败的结果数据一般称为reason


2.1.3、Promise 的基本流程

MDN Promise基本流程图

注意: 如果一个 promise 已经被兑现(fulfilled)或被拒绝(rejected),那么我们也可以说它处于已敲定(settled)状态。您还会听到一个经常跟 promise 一起使用的术语:已决议(resolved),它表示 promise 已经处于已敲定(settled)状态,或者为了匹配另一个 promise 的状态被"锁定"了。Domenic Denicola 的 States and fates 中有更多关于 promise 术语的细节可以供您参考。

简化的 Promise基本流程图


2.1.4、Promise 的基本使用

   // 1、创建一个新的 Promise 对象
    const ret = new Promise((resolve,reject) => {//执行器函数
        // 2、执行异步操作任务
        setTimeout(() => {
            const time = Date.now();//如果当前时间是偶数就代表成功,否则代表失败
            // 3.1、如果成功了,调用resolve(value)
            if(time % 2 == 0){
                resolve("成功的数据,time=" + time);
            }else{
                // 3.2、如果失败了,调用reject(reason)
                reject("失败的数据,time=" + time);
            }
        })
    })

    // ret.then(
    //     value => {//接收得到成功的 value 数据
    //         console.log("成功的回调-----------", value);
    //     },
    //     reason => {//接收得到失败的 reason 数据
    //         console.log("失败的回调-----------", reason);
    //     }
    // )

    // promise简化了对error的处理,上面的代码我们也可以这样写:
    ret.then(//接收得到成功的 value 数据
        value => {
            console.log("成功的回调-----------", value);
        }
    ).catch(//接收得到失败的 reason 数据
        reason => {
            console.log("失败的回调-----------", reason);
        }
    )

2.2、为什么要使用 Promise?

1. 指定异步回调函数的方式更加灵活:
旧的: 必须在启动异步任务前指定
promise: 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定)

// 旧的指定回调函数
function successBcak(){
    console.log("成功的回调");
}
function failBcak(){
    console.log("失败的回调");
}
function person(age, successBcak, failBcak){
    setTimeout(() => {
        if(age > 0 && age < 100){
            successBcak();
        }else{
            failBcak();
        }
    },1000)
}
console.log("aaaaaa");
person(18, successBcak, failBcak);//在调用前必须指定回调函数,而 Promise不需要
console.log("bbbbbbb");

// Promise
const p = new Promise((resolve, reject) => {
    console.log('执行 executor同步函数')
    let time = Date.now();
    setTimeout(() => {//Promise可以先执行异步任务,再指定回调函数
        if (time % 2 === 0) {
            console.log("resolve")
            resolve(time)
        } else {
            console.log("reject")
            reject(time)
        }
    }, 2000)
})

setTimeout(() => {
    p.then(value => {
        console.log('value', value)
    }, reason => {
        console.log('reason', reason)
    })
}, 3000)
console.log("aaaaaaaa");

2、 支持链式调用, 可以解决回调地狱问题
什么是回调地狱? 回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调函数执行的条件
回调地狱的缺点? 不便于阅读 / 不便于异常处理
解决方案? promise链式调用
终极解决方案? async/await

回调地狱
$.ajax({
    url:"查询用户",
    success:function(res){
        console.log(res)
        // 将数据渲染到页面上
        $.ajax({
            url: "查询到课程",
            success:function(res){
                // 获取到了课程内容的数据,渲染到页面上
                $.ajax({})
            }
        })
    }
})

promise 处理回调地狱
new Promise(function(resolve, reject){
    $.ajax({
        url:"查询用户",
        success:function(res){
                resolve(res)
        },
        error:function(err){
                reject(err)
        }   
    })
}).then(res=>{
    console.log(res)
        return new Promise(function(resolve,rejeect){
            $.ajax({
                    url: "查询到课程",
                    success:function(res1){
                            // 获取到了文章详细内容的数据,渲染到页面上
                            resolve(res1)   
                    },
                    error:function(err){
                        reject(err)
                    }   
            })
        })
}).then(res=>{
    console.log(res)
    return new Promise(function(resolve,rejeect){
            $.ajax({
                    url: "查询到分数",
                    success:function(res1){
                            // 获取到了文章详细内容的数据,渲染到页面上
                            resolve(res1)   
                    },
                    error:function(err){
                        reject(err)
                    }   
            })
        })
}).then(res=>{
    console.log(res)
})

封装一下 Promise
function get(url) {
    return new Promise((resolve, reject) => {
        $.ajax({
            url: url,
            success: function (data) {
                resolve(data);
            },
            error: function (err) {
                reject(err)
            }
        })
    });
}

//调用封装后的方法 
get("查询用户")
    .then((data) => {
        console.log("用户查询成功~~~:", data)
        return get("查询到课程);
    })
    .then((data) => {
        console.log("课程查询成功~~~:", data)
        return get("查询到分数);
    })
    .then((data) => {
        console.log("课程成绩查询成功~~~:", data)
    }).catch((err) => {
        console.log("出现异常", err)
    });


async function foo() {
  try {
    const result = await doSomething();
    const newResult = await doSomethingElse(result);
    const finalResult = await doThirdThing(newResult);
    console.log(`Got the final result: ${finalResult}`);
  } catch(error) {
    failureCallback(error);
  }
}

2.3、如何使用 Promise?(常用的 Promise API)

Promise.all:(promises) =>{}
promises:包含n个promise的数组
重点:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败

Promise.all([p1,p2,......]).then(values => {
    console.log(values);
}).catch(error => {
    consloe.log(error);
})

开发中常用的Promise.all场景,获取两个接口成功的返回值后,再执行下面的操作

function p1(){
    return new Promise((resolve) => {
        setTimeout(() => {//接口一
            resolve("p1")
        },2000)
    })
}
function p2(){
    return new Promise((resolve,reject) => {
        setTimeout(() => {//接口二
            resolve("p2")
        },1000)
    })
}
Promise.all([p1(),p2()]).then(values =>{
    console.log(values)
}).catch(error =>{
    console.log("err",error);
})

Promise.race: (promises) =>{}
promises:包含n个promise的数组
重点:返回一个新的promise,说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态

Promise.race([p1,p2,......]).then(values => {
    console.log(values);
}).catch(error => {
    consloe.log(error);
})
function p1(){
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve("p1")
            },1000)
        })
    }
    function p2(){
        return new Promise((resolve,reject) => {
            setTimeout(() => {
                reject("p2")
            },2000)
        })
    }
    Promise.race([p1(),p2()]).then(values =>{
        console.log(values)
    }).catch(error =>{
        console.log("err",error);
    })

3、async 与 await

MDN文档:
async 函数:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function
await:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await

async await 的作用
简化promise对象的使用:不用再使用then()来指定成功/失败的回调函数以同步编码(沿有回凋函数了)方式实现异步流程

3.1、async 函数

1、函数的返回值为promise对象
2.、promise对象的结果由async函数执行的返回值决定

// async 函数的返回值是一个promise对象
async function fn1(){
    // 同步
    // return 1;
    // throw 2;
    // return Promise.resolve(3);
    // return Promise.reject(4);

    // 异步
    return new Promise((resolve,reject) =>{
        setTimeout(()=> {
            resolve(5)
        },2000)
    })
}
let ret1 = fn1();
console.log(ret1);
// async 函数的返回值是一个promise对象,所以需要通过.then(()=>{})获取函数的返回值
ret1.then(res => {
    console.log("then", res)
}).catch(err =>{
    console.log("catch", err)
})

3.2、 await表达式

await 操作符用于等待一个 Promise 对象。它只能在异步函数 Promise 中使用
1、await右侧的表达式一般为 promise 对象,但也可以是其它的值
2、如果表达式是promise对象, await 返回的是 promise 成功的值(所以获取 promise 失败的返回值,会使用try{} catch (error) {})

function fn2(){
    return new Promise((resolve,reject) => {
        setTimeout(()=> {
            // resolve(11111)
            reject(22222)
        },2000)
    })
}
function fn3(){
    return 444444;
}
async function getResult(){
    try {
        let val = await fn2();//await右侧表达为promise对象,得到的结果就是promise对象成功的value
        console.log("getResult",val);
    } catch (error) {
        console.log("失败的结果",error);
    }

    // let val2 = await fn3();///await右侧表达不是promise对象,得到的结果就是它本身
    // console.log("val2",val2)
}
getResult();
async与await配合使用
function fn2(){
    return new Promise((resolve,reject) => {
        setTimeout(()=> {
            resolve(11111)
            // reject(22222)
        },2000)
    })
}
async function fn3(){//async 函数的返回值是一个promise对象
    return 444444;
}
async function getResult(){
    try {
        let val = await fn2();//await右侧表达为promise对象,得到的结果就是promise对象成功的value
        let val2 = await fn3();
        console.log("getResult",val);
        console.log("getResult",val2);
    } catch (error) {
        console.log("失败的结果",error);
    }
}
getResult();
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容