promise从入门到手写

1. 实例对象与函数对象的区别

函数对象:将函数作为对象使用时,简称为函数对象

实例对象:new 函数产生的对象,简称为对象

function Fn(){  // Fn函数   
}
const fn = new Fn() // Fn 此时是构造函数 fn 是实例对象
console.log(Fn.prototype)  // Fn 是函数对象
Fn.call({})   // Fn是函数对象
$('#test')   // jQuery 函数
$.get('/text')   // jQuery 函数对象
function Persion(params){
    
}

2. 两种类型的回调函数

  1. 同步回调

理解:立即执行,完全执行完了才结束,不会放到回调队列中去

例子:数组遍历相关的回调函数/Promise的excutor函数

const arr = [1,3,5]
arr.foreach(item => {    // 同步回调函数 
    console.log(item)
})
console.log('foreach()之后')
  1. 异步回调

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

setTimeout(()=>{
    console.log('timeout callback()')
},0)
console.log('setTimeout()之后')

3. JS 的 Error 处理

  1. 错误的类型

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

    以上这些错误类型相信大家都见过,这里不再使用代码演示

  2. 错误处理

    • 捕获错误: try ... catch
    • 抛出错误:throw error
       try {
           let d
           console.log(d.xxx)
       }catch (e){
           console.log(e.message)
       }  // 控制台 打印Cannot read property 'xxx' of undefined 不会影响后续代码的执行
    console.log(1)  //  1
    

    主动抛出错误

    function doSomething(){
        if(Date.now()%2===1){
            console.log('当前时间为奇数,可执行任务')
        }else{
            throw new Error('当前时间为偶数,无法执行任务')
        }
    }
    // 错误有执行者去处理
    try{
        doSomething();
    }catch(e){
        alret(e.message)
    }
    //  弹窗 当前时间为偶数,无法执行任务
    
  1. 错误对象

    • message属性:错误相关信息
    • stack 属性:函数调用栈记录信息

4. promise 的理解与使用

  1. promise 的理解
    • 抽象表达:Promise是JS中进行异步编程的新的解决方案(旧的是纯回调式解决方案)
    • 具体表达:
      1. 从语法上来讲:Promise是一个构造函数
      2. 从功能上来说:promise是一个用来封装一个异步操作并且可以获取其结果的对象
  2. promise 的状态的改变
    1. pending 变为 resolved (执行了 resolve 函数)
    2. pending 变为 rejected (执行了 reject 函数)

    说明:只有这2种,且一个promise对象只能改变一次无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般称为vlaue,失败的结果数据一般称为reason

    1. promise 的基本流程
Promise基本流程.png
    // 创建一个新的 promise 对象
    const p = new Promise((resolve, reject) => {   // 执行器函数
        // 开始执行异步操作
        setTimeout(() => {
            const time = Date.now()  // 当前时间为偶数就代表成功
            if (time % 2 === 0) {
                // 成功则调用  resolve(value)
                resolve('成功的回调,time = ' + time)
            } else {
                // 失败则调用  reject(reason)
                reject('失败的回调,time=' + time)
            }
        }, 1000)
    })
    p.then(
        value => {   // 接收得到成功的value数据   onResolved
            console.log('成功的回调', value)
        },
        reason => {  // 接收得到失败的reason数据    onRejected
            console.log('失败的回调', reason)
        }
    )
  1. 为什么要使用promise
    1. 指定的回调函数的方式更加灵活

      • 旧的方式:回调函数必须在启动异步任务前指定
          // 成功的回调
          function successCallback(result) {
              console.log('声音文件创建成功' + result)
          }
      
          // 失败的回调
          function failureCallback(error) {
              console.log('声音文件创建失败' + error)
          }
      
          // 使用纯回调函数
          createAudioFailAsync(audioSettings, successCallback, failureCallback)
      
      • promise:启动异步任务 => 返回 promise 对象 => 给 promise 对象绑定回调函数 (甚至可以在异步任务结束后指定/多个)
      const promise = createAudioFileAsync(audioSettings)
      setTimeout(() => {
          promise.then(successCallback, failreCAllback)
      }, 3000)
      
    2. 支持链式调用,可以解决回调地狱的问题

      • 什么是回调地狱

    回调函数的嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件

    • 不便于阅读
    • 不便于异常的处理

    回调地狱

        doSomething(function (result) {
            doSomethingelse(result, function (newresult) {
                doThirdThing(newresult, function (finalResult) {
                    console.log('Got the final result' + finalResult)
                }, failureCallback)
            }, failureCallback)
        }, failureCallback)   // 纯回调函数的回调地狱
    

    使用 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 function request(){
            try {
                const result = await doSomething()
                const newResut = await doSomethingElse(result)
                const finalResult = await doThirdThing(newResut)
                console.log('Got the final result:' + finalResult)
            }catch (error){
                failureCallback(error)
            }
        } // 使用async/await 没有回调函数
    
  2. promise 具体语法使用
    1. promise 构造函数:Promise(excutor){}

      • excutor 函数:执行器函数(resolve,reject) => {}
      • resolve 函数:内部定义成功时我们调用的函数 value => {}
      • reject 函数:内部定义失败时我们调用的函数 reason => {}

      说明:excutor 会在 Promise 内部立即同步回调,异步操作在执行器中执行

    2. Promise.prototype.then 方法:(onResolved,onRejected) => {}

      • onResolved 函数:成功 的回调函数 (value) => {}
      • onRejected函数:失败的回调函数 (reason) => {}

      说明:指定用于得到成功value的成功回调和用于得到失败reason,并返回一个新的promise对象

    3. Promise.prototype.catch方法:(onRejected) => {}

      • onRejected 函数:失败的回调函数 (reason) => {}

      说明:then()的语法糖,相当于:then(undefined,onRejeccted)

    4. Promise.resolve方法:(value) => {}

      • value:成功的数据或者promise对象

      说明:返回一个成功或失败的promise对象

    5. Promise.reject 方法:(reason)=>{}

      • reason:失败的原因

      说明:返回一个失败的promise对象

      以上 4 5 两条只是提供了创建失败或成功的promise对象的简洁的语法罢了

    6. Promise.all 方法:(promises) => {}

      • promise:包含n个promise的数组

      说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败

    7. Promise.race 方法:(promise) => {}

      • promise:包含n个promise的数组

      说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态

简单的使用例子

    new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve('成功的回调')
            // reject('失败的回调')
        }, 1000)
    }).then(value => {
        console.log('onResolved1()', value)
    }).catch(
        reason => {
            console.log('onRejected1()1',reason)
        }
    )
// 在执行器函数里面调用成功或失败的回调函数
// 产生一个成功值为1的promise对象
const p1 = new Promise((resolve, reject) => {
    resolve(1)
})
// 上面的语法糖
const p2 = Promise.resolve(2)   // 产生一个成功并且结果为2的promise对象
const p3 = Promise.reject(3)    // 产生一个是失败并且原因为3的promise对象
p1.then(value => {console.log(value)})   
p2.then(value=>{console.log(value)})
p3.then(null,reson=>{console.log(reason)})

const pAll = Promise.all([p1,p2,p3]) // 所有promise对象成功时才返回成功的promise,显然此时失败了
pAll.then(value => {
    consloe.log('all onResolved()',value)
},
         reason => {
    console.log('all onRejected():',reason)    // 打印 all onRejected(): 3
})
    const pAll = Promise.all([p1, p2]) // 所有promise对象成功时才返回成功的promise,显然此时成功了 
    pAll.then(values => {
            console.log('all onResolved()', values)   // 打印成功的数组 all onResolved() (2) [1, 2] 结果数组顺序严格按照参数顺序
        },
        reason => {
            console.log('all onRejected():', reason)    
        })

promise.race

const pRace = Promise.race([p1, p2, p3])  // 返回第一个promise的结果状态  看谁先完成
pRace.then(value => {
    consloe.log('race onResolved()', value)
},
           reason => {
    console.log('race onRejected():', reason)    // 打印 1
})

5. promise 的几个关键问题

  1. 如何改变promise的状态
    • resolve(value):如果当前是pending就会变为resolved
    • reject(reason):如果当前是pending就会变为rejected
    • 抛出异常:如果当前时pending就会变为rejected
const p = new Promise((resolve, reject) => {
    // resolve(1)  // promise 变为resolved状态
    // reject(2)   // promise 变为rejected 状态
    // throw new Error('出错了')  // 抛出异常 promise 变为 rejected 状态
    throw 3     // 可抛出任何数据
})
p.then(value => {

},
       reason => {
    console.log('reason', reason)   // 打印 3 promise 变为rejected 状态
})
p.then(value => {

},
       reason => {
    console.log('reason2',reason)  // 打印 3 promise 变为rejected 状态
})    // 两次失败回调都执行  成功也一样
  1. 一个promise指定多个成功/失败回调函数,都会调用吗?

    • 当promise改变为对应状态时都会调用
  2. 改变promise 状态和指定回调函数谁先谁后?

    • 都有可能,正常情况下是先指定回调函数再改变状态,但也可以先改状态再指定回调函数

    这段代码的执行结果

       new Promise((resolve, reject) => {
            resolve(1)
        }).then(value => {
                console.log('onResolved():', value)
            },
            reason => {
                console.log('onRejected():', reason)
            }).then(value => {
                console.log('onResolved():', value)
            },
            reason => {
                console.log('onRejected():', reason)
            })//   先打印  1  再打印 undefined 因为第一个.then产生的promise并没有得到数据
    

    先指定回调函数,再改变状态

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

    先改变状态,再执行回调

    new Promise((resolve, reject) => {
        resolve(1)     // 先改变状态(同时指定数据)
    }).then(   // 后指定回调函数,异步执行回调函数
        value => {
            console.log('value2',value)
        },
        reason => {
            console.log('reason',reason)
        }
    )
    // 或者
    const p = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1)     // 后改变的状态
        }, 1000)
    })
    setTimeout(() => {
        p.then(   // 先指定回调函数
            value => {
            },
            reason => {
                console.log('reason')
            }
        )
    }, 2000)
    
    promise.then()返回的新promise的结果状态由什么决定呢?

    答 : 简单表达 : 由then()指定的回调函数的返回执行结果决定

    详细表达 :

    1. 如果抛出异常,新promise变为rejected,reason为抛出的异常
    2. 如果返回的是非promise的任意值,新promise变为resolved,value为返回的值
    3. 如果返回的是另一个新promise,此promise的结果就会成为新promise的结果
    new Promise((resolve, reject) => {
        resolve(1)
    }).then(value => {
        console.log('onResolved():', value)
        // return 2
        // return Promise.resolve(3)
        // return Promise.resolve(4)
        throw 5
    },
            reason => {
        console.log('onRejected():', reason)
    }).then(value => {
        console.log('onResolved():', value)
    },
            reason => {
        console.log('onRejected():', reason)
    })
    
    如何先改变状态再指定回调?
    1. 在执行器中直接调用resolve/reject函数
    2. .then延迟执行
  3. promsie 如何串联多个操作任务

    • promise 的then()返回一个新的promise 对象,可以继续使用.then()的链式调用
    • 通过then的链式调用串连多个同步/异步任务
    new Promise((resolve, reject) => {
        resolve(1)
    }).then(value => {
        console.log('任务1的结果:', value)
        console.log('执行任务2(同步)')
        return 2
    }).then(
        value => {
            console.log('任务2的结果:', value)

            return new Promise((resolve, reject) => {
                //  启动异步任务3
                setTimeout(() => {
                    console.log('执行异步任务')
                    resolve(3)
                }, 1000)
            })
        }
    ).then(value => {
        console.log('任务3的结果:', value)
    })
// 打印结果

//执行异步任务1
 //index.html?_ijt=nk2cs3hseqha5cn1mdsgo8pbij:151 任务1的结果: 1
//index.html?_ijt=nk2cs3hseqha5cn1mdsgo8pbij:152 执行任务2(同步)
//index.html?_ijt=nk2cs3hseqha5cn1mdsgo8pbij:156 任务2的结果: 2
//index.html?_ijt=nk2cs3hseqha5cn1mdsgo8pbij:161 执行异步任务
//index.html?_ijt=nk2cs3hseqha5cn1mdsgo8pbij:167 任务3的结果: 3

// .then回调函数中异步任务需要使用new Promsie 封装起来才能传递到下一个.then

失败的回调会逐级传递

new Promise((resolve, reject) => {
    reject(1)
}).then(value => {
    console.log('onResolved1', value)
    return 2
    // 这里相当于 reason => {throw reason}
}).then(value => {
        console.log('onResolved2()', value)
    // 这里相当于 reason => {throw reason}
}).then(value => {
    console.log('onResolved3()', value)
    // 这里也相当于 reason => {throw reason}
}).catch(reason => {
    console.log('onRejected1()', reason)
})    // onRejected1() 1
因此.then()返回的promise对象只有抛出异常或者返回一个失败的promsie才会失败
new Promise((resolve, reject) => {
    reject(1)
}).then(value => {
    console.log('onResolved1', value)
    return 2
}).then(
    value => {
        console.log('onResolved2()', value)
    }
).then(value => {
    console.log('onResolved3()', value)
}).catch(reason => {
    console.log('onRejected1()', reason)
}).catch(reason => {
    return new Promise(() => {   // 此时中断promise连
    })
}).then(value => {
    console.log('onResolved3()', value)
},
        reason => {
    console.log('onRejected2()', reason)
})

6. 自定义promise

码云地址:https://gitee.com/Coder-XO/myPromise_ES5function/blob/main/src/promise.js

代码里面有详细的注释

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

推荐阅读更多精彩内容