20200627 Promise 笔记

一、函数对象与实例对象的区别

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

function Fn{
}

let fn = new Fn()
//此时,Fn是构造函数,fn是实例对象(简称为对象)

console.log(Fn.prototype)
//括号()的左边是函数, . 点的左边是对象
//而此时已知 Fn 是一个函数,所以此处的 Fn 就是一个函数对象

那么什么时候才称一个函数为函数对象呢?
要知道函数本身就是一个对象,但是函数加() 的形式,并不能体现它的对象特点,而是要加点 . 使用时。
所以,在将一个函数作为对象使用时,那就称它为函数对象。
例:

Fn.bind({})
//此时,Fn就是一个函数对象,是在调用Fn函数对象的bind方法。

二、两种类型的回调函数

1、什么样的函数是回调函数?(回调函数的定义):

同时满足三个条件:

  • 我自己定义的函数
  • 我没有调用
  • 被执行了

2、回调函数的分类

  • 同步回调
    理解:立即执行,完全执行完了才结束,不会放回回调队列中
    例子:数组遍历相关的回调函数,promise的excutot函数
  • 异步回调
    理解:不会立即执行,会放回回调函数队列中将来执行
    例子:定时器回调,ajax回调,promise的成功或失败的回调。
1、同步回调函数例子
let arr = [1,2,3,4,5]
arr.forEach(item => {
    console.log()
     //遍历的回调函数,同步回调函数,不会放入队列中,会直接执行完
})
console.log('forEach()之后')

//1,2,3,forEach之后



2、异步回调函数例子
setTimeout(()=>{ 
    //异步回调函数,会放入队列中将来执行
    console.log('timeout callback()')
},0)
console.log('setTimeout()之后')

//setTimeout()之后,timeout callback()

三、JS 中的 error 处理

1、错误的类型

  • Erro :所有错误的父类型
  • ReferenceError:引用的变量不存在
console.log(a)
//Uncaught ReferenceError: a is not defined
  • TypeError:数据类型不正确的错误
let b = null
b.xxx()

// Uncaught TypeError: Cannot read property 'xxx' of null
  • RangeError:数据值不在其所允许的范围内
function fn(){
    fn()
}
fn()
//Uncaught RangeError: Maximum call stack size exceeded
  • SyntaxError :语法错误
let number = """"
//Uncaught SyntaxError: Unexpected string

2、错误处理

  • 捕获错误: try ... catch
  • 抛出错误: throw error

捕获错误:

try{
    let a 
    console.log(a.xxx)
} catch(error){
    console.log(error.message)
    console.log(error.stack)
}
console.log('此处依然会执行,因为上面的错误已经被捕获处理了')

//error 中默认有两个属性,,message 和 stack
//message :提示文本
//stack :包括提示文本和相关信息,能记录调用执行的一些信息

抛出错误:

//抛出错误:throw error
function something(){
    if(Date.now()%2===1){
        console.log('当前时间为奇数,可以执行任务')
    }else{
        throw new Error('当前时间为偶数,无法执行任务')
    }
}
//抛出错误之后要捕获处理异常
try{
    something()
} catch(error){
    alert(error.message)
}

3、错误对象
message :提示文本
stack :函数调用栈记录信息,包括提示文本和相关信息,能记录调用执行的一些信息


四、promise 的理解和使用

1、什么是promise?

1.1 抽象表达:promise是JS中进行异步编程的新的解决方案。
1.2 具体表达:
(1)从语法上来说:promise是一个构造函数
(2)从功能上来说:promise对象用来封装一个异步操作并能获取其结果

2、promise的状态改变

2.1 promise有三种状态,
第一种:初始为 pending
第二种:pending 变为 resolved
第三种:pending 变为 rejected
说明:除了初始值之外只有这两种,且一个 promise 对象只能改变一次,无论变为成功还是失败,都会有一个结果数据。成功的结果数据一般称为value,失败的结果数据一般称为reason

3、promise的基本流程

1.png

4、promise的基本使用

<script>
        //1、创建一个promise对象
        const p = new Promise((resolve, reject) => { //执行器函数,执行异步操作
            //2、执行异步操作
            setTimeout(() => {
                const time = Date.now()//当前时间为偶数代表成功,反之失败
                if (time % 2 == 0) {
                    //3.1、成功了,调用resolve(value),调用时可以传入对应的数据,一般称为value
                    resolve('成功,time=' + time)
                } else {
                    //3.2、失败了,调用reject(reason),调用时可以传入对应的数据,一般称为reason
                    reject('失败,time=' + time)
                }
            }, 1000)
        })
        p.then(
            value => { //这个函数的专业称谓是: onResolved
                //接收得到成功的value数据
                console.log('成功的回调', value)
            },
            reason => { //这个函数的专业称谓是: onRejected
                //接收得到失败的reason数据
                console.log('失败的回调', reason)
            }
        )
    </script>

5、为什么要使用promise?

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

  • 支持链式调用,可以解决回调地狱问题

5.1 什么是回调地狱?

  • 回调函数嵌套使用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件
    例:
       //回调地狱,多个串联的异步操作。不便于阅读,异常处理也比较麻烦
        doSomething(function (result) {
            doSomethingElse(result, function (newResult) {
                doThirdThing(newResult, function (finalResult) {
                    console.log('Got the final result:' + finalResult)
                }, failureCallback)
            }, failureCallback)
        }, failureCallback)

解决方案:promise链式调用

        //使用promise的链式调用解决回调地狱
        doSomething()
            .then(function (result) {
                return doSomethingElse(result)
            })
            .then(function (newResult) {
                return doThirdThing(newResult)
            })
            .then(function (finalResult) {
                console.log('Got the final result:' + finalResult)
            })
            .catch(failureCallback)

终极解决方案:async / await

        //async / await :回调函数的终极解决方案
        async function request() {
            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)
            }
        } 

6、如何使用promise?

6.1、promise的API说明
  • 1、Promise 构造函数
Promise(executor){}

executor函数:同步执行 (resolve,reject)=>{}
resolve函数:内部定义成功时我们调用的函数 value=>{}
reject函数:内部定义失败时我们调用的函数 reason=>{}
说明:executor会在 Promise内部立即同步回调,异步操作在执行器中执行。
  • 2、Promise.prototype.then 方法:
 (onResolved,onRejected)=>{}

onResolved 函数:成功的回调函数 (value)=>{}
onRejected 函数:失败的回调函数 (reason)=>{}
说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调,
      返回一个新的promise对象
  • 3、Promise.prototype.catch 方法:
 (onRejected)=>{}

onRejected 函数:失败的回调函数 (reason)=>{}
说明:then() 的语法糖,相当于:then(undefined,onRejected)
  • 4、Promise.resolve 方法:
(value)=>{}

value:成功的数据或promise对象
说明:返回一个成功/失败的promise对象
  • 5、Promise.reject 方法:
 (reason)=>{}

reason:失败的原因
说明:返回一个失败的promise对象
  • 6、Promise.all 方法:
(promise)=>{}

promise:包含n个promise的数组
说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败
  • 7、Promise.race 方法:
(promise)=>{}

promise:包含n个promise的数组
说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态。
例1:

基本使用语法:

        new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('成功的数据')
                // reject('失败的数据')
            }, 1000)
        }).then(
            value => {
                console.log('onResolved()1', value)
            }
        ).catch(
            reason => {
                console.log('onRejected()1', reason)
            }
        )
例2:

产生一个成功值为1的promise对象

        写法1:
        const p1 = new Promise((resolve, reject) => {
            resolve(1)
        })
        //拿到结果:
        p1.then(value => { console.log(value) })


        写法2(语法糖):
        const p2 = promise.resolve(2)
        //拿到结果:
        p2.then(value => { console.log(value) })


产生一个失败值为3的promise对象

        const p3 = promise.reject(3)
        //拿到结果:
        p3.then(null,reason=> { console.log(reason) })
        //或:
        p3.catch(reason=> { console.log(reason) })

例3:

Promise.all()


        // const pAll = Promise.all([p1, p2, p3])
        const pAll = Promise.all([p1, p2])
        pAll.then(
            values => {
                console.log('all onResolved', values)
                //values 就是成功值,一个数组
                //打印出:all onResolved (2) ["p1成功", "p2成功"]
            },
            reason => {
                console.log('all onRejected', reason)
                //reason 就是失败值
                //打印出: all onRejected p3失败
            }
        )

//只有全部成功才算成功,只要有一个失败就算失败
例4:

Promise.race()


        const pRace = Promise.race([p1, p2, p3])
        pRace.then(
            value => {
                console.log('race onResolved', value)
                //只看第一个完成的promise的结果状态,就是最终的结果状态
            },
            reason => {
                console.log('race onRejected', reason)
                //reason 就是失败值
            }
        )

//第一个完成的promise成功就算成功,第一个完成的是失败就算失败

7、promise的几个关键问题

1、如何改变promise的状态?

  • (1) resolve(value):如果当前是 pending就会变为 resolved
  • (2) reject(reason):如果当前是pending 就会变为 rejected
  • (3) 抛出异常时:如果当前是 pending就会变为 rejected
例:
const p = new Promise((resolve,reject)=>{
    resolve(1)  //promise变为resolved成功状态
    reject(2)   //promise变为rejected失败状态
    throw new Error('出错了')  //抛出异常,promise变为rejected状态,
      //reason值为抛出的error
    throw 3      //抛出异常,promise变成rejected状态,reason值为抛出的 3
})

2、一个promise指定多个成功/失败回调函数,都会调用吗?

  • promise改变为对应的状态时都会调用

3、改变promise状态和指定回调函数谁先谁后?

  • (1)都有可能,正常情况下是先指定回调函数再改变状态,但也可以先改变状态再指定回调函数
  • (2)如何先改变状态再指定回调?
    在执行器中直接调用 resolve() / reject()
    使用例如 setTimeout 延迟更长时间才调用 then()
  • (3)什么时候才能得到数据?
    如果先指定回调,那当状态发生改变时,回调函数就会调用,得到数据。
    如果先改变状态,那当指定回调时,回调函数就会调用,得到数据。
例:
//1、常规:先指定回调函数,后改变状态
new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve(1)//后改变状态(同时指定数据),异步执行回调函数
    },1000)
}).then( //先指定回调函数,保存当前指定的回调函数
    value =>{},
    reason =>{console.log('reason',reason)}
)


//2、先改变状态,后指定回调函数
new Promise((resolve,reject)=>{
    resolve(1) //先改变状态(同时指定数据)
}).then(  //后指定回调函数,异步执行回调函数
    value =>{console.log('value',value)},
    reason =>{console.log('reason'.reason)}
)

4、promise.then() 返回的新promise的结果状态油什么决定?
(1)简单表述: 由 then()指定的回调函数执行的结果决定
(2)详细表述:

  • 如果抛出异常,新promise变为rejected,reason为抛出的异常
  • 如果返回的是非promise的任意值,新promise变为resolved,value为返回的值
  • 如果返回的是另一个新的promise,此promise的结果会成为新的promise的结果
new Promise((resolve,reject)=>{
    resolve(1)
}).then(
    value =>{
        console.log('onResolved1()',value)
        //return 2  --- 第二个then成功,值为2
        //return Promise.resolve(3)  --- 第二个then成功,值为3
        //return Promise.reject(4)  --- 第二个then失败,值为4
        //throw 5   --- 第二个then失败,值为5
    },
    reason =>{
        console.log('onRejected1()',reason)
    }
).then(
    value =>{
        console.log('onResolved1()',value)
    },
    reason =>{
        console.log('onRejected1()',reason)
    }
)

5、promise如何串联多个操作任务?
(1)promise的then()返回一个新的promise,可以看成then()的链式调用。
(2)通过then的链式调用串联多个同步/异步任务。

        new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('执行任务1(异步)')
                resolve(1)
            }, 1000)
        }).then(
            value => {
                console.log('任务1的结果是:', value)
                console.log('执行任务2(同步)')
                return 2
            }
        ).then(
            value => {
                console.log('任务2的结果是:', value)
                //因为是异步执行,所以要封装到一个新的promise里面
                //因为promise就是用来封装异步操作的
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        console.log('执行任务3(异步)')
                        resolve(3)
                    }, 1000)
                })
            }
        ).then(
            value => {
                console.log('任务3的结果是:', value)
            }
        )

6、promise异常传透?
(1)当使用promise的then链式调用时,可以在最后指定失败的回调
(2)前面任何操作出了异常,都会传到最后失败的回调中处理。

new Promise((resolve, reject) => {
    // resolve(1)
    reject('我是我是我是')
}).then(
    value => {
        console.log('onResolved1()', value)
        return 2
    }
    //没有写失败的回调函数,相当于默认写了一个:
    // reason => {throw reason}
    //或者是写为:
    // reason => Promise.reject(reason)
).then(
    value => {
        console.log('onResolved2()', value)
        return 3
    }
).then(
    value => {
        console.log('onResolved3()', value)
    }
).catch(reason => {
    console.log('onRejected1()', reason)
})
//一层一层往下传,如果在某一次处理了,就不会再往下传。

7、中断promise链
(1)当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数
(2)办法:在回调函数中返回一个pending状态的promise对象

new Promise((resolve, reject) => {
    // resolve(1)
    reject('我是我是我是')
}).then(
    value => {
        console.log('onResolved1()', value)
        return 2
    }
).then(
    value => {
        console.log('onResolved2()', value)
        return 3
    }
).then(
    value => {
        console.log('onResolved3()', value)
    }
).catch(reason => {
    console.log('onRejected1()', reason)
    return new Promise(() => { })  
    //返回一个为pending状态的promise,中断promise链
}).then(
    value=>{
        console.log('catch下面的',value)
    },
    reason=>{
        console.log('catch下面的',reason)
    }
)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354