Part1.模塊一:函數(shù)式編程與 JS 異步編程、手寫 Promise

簡答題

一、談?wù)勀闶侨绾卫斫釰S異步編程的,EcentLoop、消息隊列都是做什么的,什么是宏任務(wù),什么是微任務(wù)?

  • JS 異步編程:
    • 解答:
      ? ? ? ?JavaScript語言的執(zhí)行環(huán)境是單線程,單線程是指一次只能完成一個任務(wù),如果有多個任務(wù),則需要排隊,等待前一個任務(wù)完成后,才能開始后一個任務(wù)。基于這種原因而產(chǎn)生了兩種執(zhí)行任務(wù)的模式:同步模式和異步模式,且隨著JavaScript面臨的需求要來越多,它可以運行在瀏覽器、服務(wù)器上等,為了滿足這些需求,使得JavaScript的規(guī)模和復(fù)雜性也在持續(xù)增長,所以JavaScriptd中的異步編程也在不斷地調(diào)整,往更友好的方向發(fā)展,JavaScript異步編程經(jīng)歷了回調(diào)函數(shù)、Promise、生成器函數(shù)Generator、以及現(xiàn)在Async/Await等幾個發(fā)展階段。
      ? ? ? ?在第一階段使用的是回調(diào)函數(shù),它是最基本的異步操作方式,以Ajax請求最為常見,它是最簡單、容易理解和實現(xiàn)的異步編程;但是不利于代碼的閱讀和維護(hù),各部分之間高度耦合,使得程序結(jié)構(gòu)混亂,流程難以追蹤,每個任務(wù)只能指定一個回調(diào)函數(shù),不能使用try catch捕獲錯誤,不能直接return,且極易出現(xiàn)回調(diào)地獄導(dǎo)致的調(diào)式困難,以及控制反轉(zhuǎn)導(dǎo)致的一系列信任問題
ajax(urlA, () => {
    // 處理邏輯
    ajax(urlB, () => {
        // 處理邏輯
        ajax(urlC, () => {
            // 處理邏輯
        })
    })
})

? ? ? ?在第二階段引入了Promise,它是ES6推出的一種異步編程的解決方案。Promise是承諾的意思,這個承諾在未來某個時刻會有一個確定的答復(fù),該承諾有三種狀態(tài):等待中(pending)、完成(resolved)、拒絕(rejected)。Promise是個構(gòu)造函數(shù),接受一個函數(shù)作為參數(shù)。作為參數(shù)的函數(shù)有兩個參數(shù):resolve和reject,分別對應(yīng)完成和拒絕兩種狀態(tài)。我們可以選擇在不同時候執(zhí)行resolve或reject去觸發(fā)下一個動作,執(zhí)行then方法里的函數(shù)。Promise實現(xiàn)了鏈?zhǔn)秸{(diào)用,每次調(diào)用then之后返回的都是一個Promise對象,如果在then使用了return,return返回的值會被Promise.resolve()包裝。它很好的解決了回調(diào)函數(shù)中的回調(diào)地獄,解決了控制反轉(zhuǎn)導(dǎo)致的信任問題,將代碼的執(zhí)行主動權(quán)拿了回來;但是Promise一旦狀態(tài)從等待改變?yōu)槠渌麪顟B(tài)就不再可變了,還有如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯誤。

new Promise((resolve, reject) => {   
    console.log('new Promise');   
    resolve('success'); }) 
    console.log('end');

? ? ? ?第三階段使用生成器函數(shù)Generator,它是一種特殊的函數(shù),其最大的特點是控制函數(shù)的執(zhí)行。可以讓我們用同步的方式寫代碼,可以分步執(zhí)行并得到異步操作的結(jié)果,能夠知曉異步操作的過程,以及切入修改異步操作的過程。但是需要手動去控制next(...),將回調(diào)成功的返回數(shù)據(jù)送回JavaScript的主流程中;

function *foo(x) {
  let y = 2 * (yield (x + 1));
  let z = yield (y / 3);
  return (x + y + z);
}
let it = foo(5);
console.log(it.next());   // => {value: 6, done: false}
console.log(it.next(12)); // => {value: 8, done: false}
console.log(it.next(13));// => {value: 42, done: true}

? ? ? ?最新階段的Async/Await異步處理編程,其中async函數(shù)返回一個 Promise 對象,就是將函數(shù)返回使用Promise.resolve(),和then處理返回值一樣,可以使用then方法添加回調(diào)函數(shù)。await后邊一般跟Promise對象,async函數(shù)執(zhí)行遇到await后,等待后面的Promise對象的狀態(tài)從pending變成resolve后,將resolve的參數(shù)返回并自動往下執(zhí)行直到下一個await或結(jié)束。它解決了Generator需要手動控制next(...)執(zhí)行的問題。但它存在一個缺陷是如果多個異步代碼沒有依賴性卻使用了await會導(dǎo)致性能降低。

async function test() {
    console.log('1')
}
console.log(test)   // Promise {<resolve>: "1"}

? ? ? ?整個異步過程都是通過內(nèi)部的消息隊列和事件循環(huán)實現(xiàn)的
? ? ? ?每個階段的突破都是為了解決現(xiàn)有階段的技術(shù)問題,異步編程的發(fā)展也是一個循序漸進(jìn)的過程。

  • EventLoop、消息隊列:
    • 解答:
      ? ? ? ?事件循環(huán)機制和消息隊列的維護(hù)是由事件觸發(fā)線程控制的,事件觸發(fā)線程是由瀏覽器渲染引擎提供的,它會維護(hù)一個消息隊列
      ? ? ? ?EventLoop是事件循環(huán),主要負(fù)責(zé)監(jiān)聽調(diào)用棧(執(zhí)行棧)和消息隊列(任務(wù)隊列),一旦調(diào)用棧中所有的任務(wù)都結(jié)束了,事件循環(huán)就會從消息隊列中取出第一個回調(diào)函數(shù),然后壓入到調(diào)用棧中,一旦消息隊列中發(fā)生了變化,事件循環(huán)就會監(jiān)聽到
      ? ? ? ?消息隊列:消息隊列是類似隊列的數(shù)據(jù)結(jié)構(gòu),遵循先入先出(FIFO)的規(guī)則。如果把調(diào)用棧理解為正在執(zhí)行的工作表,那么消息隊列則可以理解成待辦的工作表,JS引擎會先做完調(diào)用棧中所有的任務(wù),然后通過事件循環(huán)從消息隊列中再取一個任務(wù)出來繼續(xù)執(zhí)行,以此類推,整個過程隨時可以往消息隊列中再去放任務(wù),這些任務(wù)在消息隊列中會排隊等待事件循環(huán)
      ? ? ? ?事件循環(huán)機制:
      ? ? ? ? ? ?1.JS引擎線程會維護(hù)一個執(zhí)行棧,同步代碼會依次加入執(zhí)行棧中,然后依次執(zhí)行并出棧
      ? ? ? ? ? ?2.JS引擎線程遇到異步函數(shù),會將異步函數(shù)交給相應(yīng)的WebApi,并繼續(xù)執(zhí)行后面的任務(wù)
      ? ? ? ? ? ?3.WebApi會在條件滿足的時候,將異步對應(yīng)的回調(diào)加入到消息隊列中,等待執(zhí)行
      ? ? ? ? ? ?4.執(zhí)行棧為空時,JS引擎線程會去取消息隊列中的回調(diào)函數(shù)(如果有的話),并加入到執(zhí)行棧中執(zhí)行
      ? ? ? ? ? ?5.完成后出棧,繼續(xù)執(zhí)行4的操作,直至消息隊列中的回調(diào)函數(shù)為空,以上便是事件循環(huán)的機制
  • 宏任務(wù)、微任務(wù)
    • 解答:
      ? ? ? ?在JS中,有兩類任務(wù)隊列:宏任務(wù)隊列(macrotask)和微任務(wù)隊列(microtask),宏任務(wù)可以由多個,微任務(wù)只有一個
      • macrotask:主代碼塊、setTimeout、setInterval、setImmediate、I/O、UI rendering等(可以看到,事件隊列中的每一個事件都是一個 macrotask,現(xiàn)在稱之為宏任務(wù)隊列)
      • microtask:Promise、process.nextTick、Object.observer等

? ? ? ?每次執(zhí)行棧執(zhí)行的代碼即是一個宏任務(wù),包括任務(wù)隊列(宏任務(wù)隊列)中的,因為執(zhí)行棧中的宏任務(wù)執(zhí)行完后會去取任務(wù)隊列(宏任務(wù)隊列)中的任務(wù)加入執(zhí)行棧中
? ? ? ?在執(zhí)行宏任務(wù)時遇到Promise等,會創(chuàng)建微任務(wù)(.then()里面的回調(diào)),并加入到微任務(wù)隊列隊尾;微任務(wù)必然是在某個宏任務(wù)執(zhí)行的時候創(chuàng)建的,而在下一個宏任務(wù)開始之前,瀏覽器會對頁面重新渲染。同時,在上一個宏任務(wù)執(zhí)行完成后,渲染頁面之前,會執(zhí)行當(dāng)前微任務(wù)隊列中的所有微任務(wù)。
? ? ? ?執(zhí)行機制
? ? ? ? ? ?1.執(zhí)行一個宏任務(wù)(執(zhí)行棧中沒有就從消息隊列中獲取)
? ? ? ? ? ?2.執(zhí)行過程中如果遇到微任務(wù),就將微任務(wù)添加刀微任務(wù)的任務(wù)隊列中
? ? ? ? ? ?3.宏任務(wù)執(zhí)行完畢后,立即執(zhí)行當(dāng)前微任務(wù)隊列中的所有微任務(wù)(依次執(zhí)行)
? ? ? ? ? ?4.當(dāng)前宏任務(wù)執(zhí)行完畢,開始檢查渲染,然后GUI接管渲染
? ? ? ? ? ?5.喧渲染完畢后,JS引擎線程繼續(xù),開始下一個宏任務(wù)(從宏任務(wù)隊列中獲取)

代碼題

一、將下面異步代碼使用Promise的方式改進(jìn)

異步代碼.png

解答:使用Promise的方式改進(jìn)實現(xiàn)如下:

// 使用Promise改進(jìn)setTimeout異步代碼
new Promise((resolve, reject) => {
    resolve('hello')
}).then(value => {
    return value + ' ' + 'lagou' // value = hello 
})
.then(value => {
    return value + ' ' + 'I ? U'  // value = hello lagou 
})
.then(value => {
    console.log(value)   // hello lagou I ? U
})

二、基于以下代碼完成四個練習(xí)

代碼1-題目.png

代碼2-題目.png
練習(xí)1:使用組合函數(shù)fp.flowRight()重新實現(xiàn)下面這個函數(shù)

練習(xí)1-題目.png

解答:fp.flowRight()重新實現(xiàn)如下:

//練習(xí)1
let isLastInstock = fp.flowRight(fp.prop('in_stock') , fp.last)
console.log(isLastInstock(cars)) //false   即最后一條數(shù)據(jù)的in_stock屬性值為fase
練習(xí)2:使用fp.flowRight(),fp.prop(),fp.first()獲取第一個car的name

解答:獲取第一個car的name實現(xiàn)如下:

// 練習(xí)2
let getFirstCarName = fp.flowRight(fp.props('name'), fp.first)
console.log(getFirstCarName(cars))  //[ 'Ferrari FF' ]
練習(xí)3:使用幫助函數(shù)_average()重構(gòu)averageDollarValue,使用函數(shù)組合方式實現(xiàn)

練習(xí)3-題目.png

解答:重構(gòu)averageDollarValue實現(xiàn)如下:

let averageDollarValue = fp.flowRight(_average,fp.map(car => car.dollar_value))
console.log(averageDollarValue(cars))  //790700
練習(xí)4:使用flowRight寫一個sanitizeName()函數(shù),返回一個下劃線連接的小寫字符串,把數(shù)組中的name轉(zhuǎn)換為這種形式,例如:sanitizeName(["Hello World"]) => ["hello_world"]

練習(xí)4-題目.png

解答:使用flowRight寫一個sanitizeName()函數(shù)實現(xiàn)如下:

// 練習(xí)4
let _underscore = fp.replace(/\W+/g, '_')

//先遍歷數(shù)組把里面字符串轉(zhuǎn)成小寫,在遍歷數(shù)組將非單字字符轉(zhuǎn)換成下劃線
let sanitizeName1 = fp.flowRight( fp.map(_underscore), fp.map(fp.toLower))
//遍歷數(shù)組中使用fp.flowRight組合函數(shù),先轉(zhuǎn)換成小寫,再將非單字字符轉(zhuǎn)換成下劃線
let sanitizeName2 = fp.flowRight( fp.map(fp.flowRight(_underscore, fp.toLower)))
console.log(sanitizeName2(["Hello World",'LaGou Study']))  //[ 'hello_world', 'lagou_study' ]

三、基于下面提供的四個代碼,完成后續(xù)的四個練習(xí)

代碼1-題目.png

代碼2-題目.png

代碼3-題目.png
練習(xí)1:使用fp.add(x,y)和fp.map(f,x)創(chuàng)建一個能讓functor里的值增加的函數(shù)ex1

練習(xí)1-題目.png

解答:讓functor里的值增加的函數(shù)實現(xiàn)如下:

// 練習(xí)1
let ex1 = () => {
    return fp.map(fp.add(1), maybe.map(x => x)._value)
}
console.log(ex1())  //[ 6, 7, 2 ]
練習(xí)2:實現(xiàn)一個函數(shù)ex2,能夠fp.first獲取列表的第一個元素

練習(xí)2-題目.png

解答:獲取列表的第一個元素實現(xiàn)如下:

//練習(xí)2
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])

let ex2 = () => {return fp.first(xs.map(x => x)._value)}
console.log(ex2())  //do
練習(xí)3:實現(xiàn)一個函數(shù)ex3,使用safeProp和fp.first找到user的名字的首字母

練習(xí)3-題目.png

解答:使用safeProp和fp.first找到user的名字的首字母實現(xiàn)如下:

// 練習(xí)3
let safeProp = fp.curry(function (x, o){
    return Maybe.of(o[x])
})
let user = {id: 2, name: 'Albert'}
let ex3 = () => {
    return fp.first(safeProp(Object.keys(user).indexOf('name'),Object.values(user))._value)
}
console.log(ex3()) //A
練習(xí)4:使用MayBe重寫ex4,不要有if語句
練習(xí)4.png
// 練習(xí)4
let ex4 = function (n) {
    if(n){
        return(parseInt(n))
    }
}

let ex5 = n =>{
    return Maybe.of(n)._value 
}

let ex6 = n =>{
    return !!n? Maybe.of(n)._value : undefined
}
console.log(ex4(''))
console.log(ex5(''))
console.log(ex6(''))
注:ex5的寫法會在空字符串和null的時候?qū)е螺敵龊蚭x4,ex6不一樣

四、手寫實現(xiàn)MyPromise源碼

要求:盡可能還原Promise中的每一個API,并通過注釋的方式描述思路和原理

MyPromise源碼(myPromise.js)
/*由于這個狀態(tài)頻繁使用,為了使用這個常量時編輯器有代碼提示并能夠復(fù)用,故把它定義為常量*/
const PENDING = 'pending'  //等待
const FULFILLED = 'fulfilled'  //成功
const REJECTED = 'rejected'  //失敗

class MyPromise{
    //構(gòu)造函數(shù)  ==》 立即執(zhí)行執(zhí)行器  ==》 指的是傳遞過來的回調(diào)函數(shù)
    constructor(executor) {

        // 執(zhí)行器錯誤處理 當(dāng)執(zhí)行器中代碼在執(zhí)行過程中發(fā)生錯誤的時候,這個時候就讓promise狀態(tài)變成失敗
        try{
            executor(this.resolve, this.reject)
        }catch (e) {
            //捕獲執(zhí)行器的錯誤
            this.reject(e)
        }
    }

    /*狀態(tài)是每個promise對象獨有的,故因該把狀態(tài)屬性定義為實例屬性*/
    status = PENDING   //promise狀態(tài)  ===》 默認(rèn)值為等待

    //由于每個promise對象都有自己成功之后的值,都有自己失敗的原因,故應(yīng)該把這兩個屬性定義為實例的屬性
    value = undefined  //成功之后的值
    reason = undefined //失敗后的原因

    //由于then方法可能會被多次調(diào)用,聯(lián)想到數(shù)組能夠同時存儲多個函數(shù) 故在這里將屬性定義為數(shù)組
    successCallback = [] //成功回調(diào)
    failCallback = []    //失敗回調(diào)

    //定義為箭頭函數(shù)的目的:將來在直接調(diào)用某個普通函數(shù)時,這個函數(shù)的this指向是window或者undefined,為了能讓this的指向為類的實例對象即promise對象,故而使用箭頭函數(shù)
    resolve = value => {

        //為了確保狀態(tài)確定后就不可更改,需加以判斷
        //如果狀態(tài)不是等待,阻止程序向下執(zhí)行
        if(this.status !== PENDING) return

        //將狀態(tài)更改為成功
        this.status = FULFILLED

        //將傳遞過來的值進(jìn)行賦值 ==》 保存成功后的值
        this.value = value

        //判斷成功回調(diào)是否存在 如果存在 則調(diào)用并傳遞相應(yīng)的參數(shù)
        //數(shù)組中存儲了多個回調(diào)函數(shù),我們需要循環(huán)遍歷這個數(shù)組,并在循環(huán)的過程中調(diào)用這個回調(diào)函數(shù)
        //當(dāng)數(shù)組的長度不為0時,就繼續(xù)執(zhí)行循環(huán)體中的代碼
        //考慮到需要從前往后執(zhí)行,故調(diào)用數(shù)組的shift方法,把前面的回調(diào)函數(shù)彈出來
        while(this.successCallback.length) this.successCallback.shift()()
    }

    reject = reason => {

        //為了確保狀態(tài)確定后就不可更改,需加以判斷
        //如果狀態(tài)不是等待,阻止程序向下執(zhí)行
        if(this.status !== PENDING) return

        //將狀態(tài)更改為失敗
        this.status = REJECTED

        //將傳遞過來的值進(jìn)行賦值 ==》 失敗后的原因
        this.reason = reason

        //判斷失敗回調(diào)是否存在 如果存在 則調(diào)用并傳遞相應(yīng)的參數(shù)
        //數(shù)組中存儲了多個回調(diào)函數(shù),我們需要循環(huán)遍歷這個數(shù)組,并在循環(huán)的過程中調(diào)用這個回調(diào)函數(shù)
        //當(dāng)數(shù)組的長度不為0時,就繼續(xù)執(zhí)行循環(huán)體中的代碼
        //考慮到需要從前往后執(zhí)行,故調(diào)用數(shù)組的shift方法,把前面的回調(diào)函數(shù)彈出來
        while(this.failCallback.length) this.failCallback.shift()()
    }

    //then要被定義在原型對象中,并接受成功回調(diào)和失敗回調(diào)兩個參數(shù)
    then (successCallback, failCallback){

        //在調(diào)用then方法不傳遞任何參數(shù)的時候,要將最先的成功狀態(tài)依次傳遞給有回調(diào)函數(shù)的then方法
        successCallback = successCallback? successCallback : value => value
        failCallback = failCallback? failCallback : reason => {throw reason}

        //then要實現(xiàn)鏈?zhǔn)秸{(diào)用,then方法必須返回promise對象,上一個回調(diào)的返回值傳遞給下一個then成功的回調(diào)
        let promise2 = new MyPromise((resolve, reject)=>{

            //判斷狀態(tài)并調(diào)用對應(yīng)的回調(diào)函數(shù)并傳遞相應(yīng)的參數(shù)
            if(this.status === FULFILLED){

                //由于promise2還沒被實例完,在實例過程中獲取不到,故使用異步實現(xiàn),讓同步代碼先實現(xiàn),再來執(zhí)行異步代碼,這時候就能獲取到promise2
                setTimeout(()=>{

                    //then方法中的回調(diào)函數(shù)在執(zhí)行過程中報錯,這個錯誤要在下一個then的reject中捕獲到
                    try{
                        let x = successCallback(this.value)
                        resolvePromise( promise2, x, resolve, reject)
                    }catch (e) {
                        //捕獲then回調(diào)的錯誤
                        reject(e)
                    }
                }, 0)
            }else if(this.status ===REJECTED){

                //由于promise2還沒被實例完,在實例過程中獲取不到,故使用異步實現(xiàn),讓同步代碼先實現(xiàn),再來執(zhí)行異步代碼,這時候就能獲取到promise2
                setTimeout(()=>{

                    // then方法中的回調(diào)函數(shù)在執(zhí)行過程中報錯,這個錯誤要在下一個then的reject中捕獲到
                    try{
                        let x = failCallback(this.reason)
                        resolvePromise( promise2, x, resolve, reject)
                    }catch (e) {
                        //捕獲then回調(diào)的錯誤
                        reject(e)
                    }
                }, 0)
            }else{

                //說明當(dāng)前狀態(tài)等待,但不知是調(diào)用成功還是失敗的回調(diào)
                //故將成功回調(diào)和是失敗回調(diào)存儲起來
                this.successCallback.push(() => {
                    successCallback()
                    setTimeout(()=>{
                        try{
                            let x = successCallback(this.value)
                            resolvePromise( promise2, x, resolve, reject)
                        }catch (e) {
                            //捕獲then回調(diào)的錯誤
                            reject(e)
                        }
                    }, 0)
                })
                this.failCallback.push(() => {
                    setTimeout(()=>{
                        try{
                            let x = failCallback(this.reason)
                            resolvePromise( promise2, x, resolve, reject)
                        }catch (e) {
                            //捕獲then回調(diào)的錯誤
                            reject(e)
                        }
                    }, 0)
                })
            }
        });
        return promise2
    }

    //無論當(dāng)前這個promise對象最終的狀態(tài)是成功還是失敗,finally中的這個回調(diào)函數(shù)始終都會執(zhí)行一次e
    //在finally方法的后面可以鏈?zhǔn)降恼{(diào)用then方法拿到當(dāng)前這個promise對象返回的結(jié)果
    finally (callBack){
        //如何得到當(dāng)前promise對象的狀態(tài)  ==》 this.then方法可以
        return this.then(value=>{
            //在finally中return了一個promise對象
            return MyPromise.resolve(callBack()).then(()=>value)
        },reason=>{
            return MyPromise.resolve(callBack()).then(()=> {throw reason})
        })
    }

    //用于當(dāng)前promise對象失敗的情況,如果沒有傳遞失敗的回調(diào)函數(shù),則會被catch捕獲到
    catch(failCallback){
        return this.then(undefined, failCallback)
    }

    // all方法是用來解決異步并發(fā)問題的,它允許我們按照異步代碼調(diào)用的順序得到異步執(zhí)行的結(jié)果
    // all的返回值也是一個promise對象,在后面可以鏈?zhǔn)綄τ胻hen方法
    // 在all中的promise對象都是成功的,那么all最后的結(jié)果也是成功,如果有一個失敗,那么結(jié)果就是失敗的
    // 通過類調(diào)用,故為靜態(tài)方法,該方法接收一個數(shù)組作為參數(shù)
    static all (array){
        let results = []
        let index = 0

        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                results[key] = value
                index++
                // 針對異步情況下,判斷index的值和數(shù)組的長度相等,說明數(shù)組中所有的內(nèi)容都執(zhí)行完了,從而而決定是否調(diào)用resolve回調(diào)函數(shù)
                if(index === array.length){
                    resolve(results)
                }
            }

            //循環(huán)該數(shù)組
            for(let i = 0; i < array.length; i++){
                let current = array[i]
                if(current instanceof MyPromise){
                    //promise對象,若成功的話將成功的值添加到數(shù)組中,若有一個失敗的話失敗的話則將失敗的原因通過reject回調(diào)
                    current.then(value => addData(i, value), reason => reject(reason))
                }else{
                    //普通值 則直接添加到數(shù)組中
                    addData(i, array[i])
                }
            }

        })
    }

    //如果是普通值,則創(chuàng)建一個promise對象,被將這個值包裹在promise對象中,然后把創(chuàng)建出來的promise對象作為resolve方法的返回值,才能在后面鏈?zhǔn)秸{(diào)用then方法
    //如果是promise對象,則原封不動的將這個promise對象再作為resolve方法的返回值,所以才能在后面調(diào)用then方法,通過then方法的成功回調(diào)拿到這個promise對象的返回值
    static  resolve (value){
        if(value instanceof  MyPromise) return value
        return new MyPromise(resolve => resolve(value))
    }
}

//判斷 x 值時普通值還是promise對象
//如果是普通值,直接調(diào)用resolve
//如果是promise對象 查看promise對象返回的結(jié)果
//再根據(jù)promise對象返回的結(jié)果 決定調(diào)用resolve 還是調(diào)用reject
function resolvePromise(promise2, x, resolve, reject){
    //相等表示自己返回自己(Promise對象自返回),這時應(yīng)該調(diào)用reject回調(diào)函數(shù)
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if( x instanceof  MyPromise){
        //promise對象
        x.then(resolve,reject)
    }else{
        //普通值
        resolve(x)
    }
}

//導(dǎo)出當(dāng)前類
module.exports = MyPromise;

測試代碼(index.js)
/**
 * 了解promise原理及原理代碼實現(xiàn)
 * 1.Promise 就是一個類 在執(zhí)行這個類的時候 需要傳遞一個執(zhí)行器進(jìn)去 執(zhí)行器會立即執(zhí)行 故放在promise類的構(gòu)造器中
 * 2.Promise 中有三種狀態(tài) 分別為 成功(fulfilled) 失敗(rejected) 等待(pending)
 *  pending -> fulfilled
 *  pending -> rejected
 ** 一旦狀態(tài)確定就不可更改
 * 3.resolve和reject函數(shù)是用來更改狀態(tài)
 * resolve:fulfilled
 * reject:rejected
 * 4.then方法內(nèi)部做的事情就是判斷狀態(tài) 如果狀態(tài)成功 調(diào)用成功的回調(diào)函數(shù) 如果狀態(tài)失敗 調(diào)用失敗的回調(diào)函數(shù) then被定義為原型對象當(dāng)中
 * 5.then成功回調(diào)有一個參數(shù) 表示成功之后的值 then失敗回調(diào)有一個參數(shù) 表示失敗的原因
 **/
//導(dǎo)向MyPromise
const MyPromise = require('./myPromise')

//創(chuàng)建promise對象    resolve  reject 兩個函數(shù)參數(shù)
let promise = new Promise((resolve, reject) => {
    //把狀態(tài)變成成功 參數(shù)為成功后的值
    resolve()

    //把狀態(tài)編程失敗  參數(shù)為失敗的原因
    reject()
})

//需實現(xiàn)promise的then方法,要先了解其中傳遞兩個回調(diào)函數(shù)及其含義
//調(diào)用then方法時,首先要去判斷promise狀態(tài),成功的話則調(diào)用成功回調(diào)函數(shù),失敗的話則調(diào)用失敗回調(diào)函數(shù)
//由于then能被任意一個promise對象調(diào)用,故應(yīng)該將它定義在原型對象中
//then成功回調(diào)有一個參數(shù)(value) 表示成功之后的值 then失敗回調(diào)有一個參數(shù)(reason) 表示失敗的原因
promise.then(value=>{},reason=>{})


/** 1.類核心邏輯實現(xiàn)調(diào)用 **/
/*let promise1 = new MyPromise((resolve, reject) => {
    resolve('成功')

    // reject('失敗')
})

promise1.then(value => {
    console.log(value)  // 成功
}, reason => {
    console.log(reason)  // 失敗
})*/

/** 2.在Promise類中加入異步邏輯 **/
/*let promise2 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    },2000)

    // reject('失敗')
})

promise2.then(value => {
    console.log(value)  // 等待2s后輸出成功
}, reason => {
    console.log(reason)  // 等待2s后輸出失敗
})*/

/** 3.實現(xiàn)then方法多次調(diào)用添加多個處理函數(shù) **/
/*let promise3 = new MyPromise((resolve, reject) => {
    resolve('成功')

    // reject('失敗')
})

promise3.then(value => {
    console.log(1)  // 1
    console.log(value)  // 成功
}, reason => {
    console.log(reason)  // 失敗
})

promise3.then(value => {
    console.log(2)  // 2
    console.log(value)  // 成功
}, reason => {
    console.log(reason)  // 失敗
})

promise3.then(value => {
    console.log(3)  // 3
    console.log(value)  // 成功
}, reason => {
    console.log(reason)  // 失敗
})*/

/** 4./5.實現(xiàn)then方法的鏈?zhǔn)秸{(diào)用 **/
//后面then方法拿到的值實際上是上一個回調(diào)函數(shù)的返回值
/*let promise4 = new MyPromise((resolve, reject) => {
    resolve('成功')

    // reject('失敗')
})
//測試4 then中返回值的情況
promise4.then(value => {
    console.log(value)  // 成功
    return 100
}).then(value => {
    console.log(value)
})
//測試5  then中返回promise情況
function other(){
    return new MyPromise((resolve, reject) => {
        resolve('other')
    })
}
promise4.then(value => {
    console.log(value)  // 成功
    return other()
}).then(value => {
    console.log(value)  // other
})*/

/** 6.then方法鏈?zhǔn)秸{(diào)用識別Promise對象自返回 **/
/*let promise6 = new MyPromise((resolve, reject) => {
    resolve('成功')
    // reject('失敗')
})

let p6 = promise6.then(value => {
    console.log(value)
    return p6
})

p6.then(value => {
    console.log(value)  //成功
},reason => {
    console.log(reason.message)  //Chaining cycle detected for promise #<Promise>
})*/

/** 7.捕獲錯誤及then鏈?zhǔn)秸{(diào)用其他狀態(tài)代碼補充 **/
// 執(zhí)行器錯誤   then方法中的回調(diào)函數(shù)在執(zhí)行過程中報錯,這個錯誤要在下一個then的reject中捕獲到
/*let promise7 = new MyPromise((resolve, reject) => {
    // throw new Error('executor error')
    // resolve('成功')
    reject('失敗')
})

promise7.then(value => {
    console.log(value)  //成功
},reason => {
    console.log(reason.message)  //executor error
})

promise7.then(value => {
    console.log(value)  //成功
    throw new Error('then error')
},reason => {
    console.log(reason.message)  //executor error
    return 10000
}).then(value => {
    console.log(value)  // 成功  10000
    // throw new Error('then error')
},reason => {
    console.log('xxx')   //xxx
    console.log(reason.message)  //then error
})*/

/** 8.then中方法編程可選參數(shù) **/

/*let promise8 = new MyPromise((resolve, reject) => {
    resolve('成功')
    // reject('失敗')
})
promise8.then().then().then(value =>
    console.log(value) //成功  失敗
)*/


function p1() {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve('p1')
        }, 2000)
    })
}

function p2() {
    return new MyPromise((resolve, reject) => {
        // resolve('p2')
        reject('失敗')
    })
}

/** 9.promise.all方法的實現(xiàn) **/
/*MyPromise.all(['a', 'b', p1(), p2(), 'c']).then(result =>
    console.log(result)  // [ 'a', 'b', 'p1', 'p2', 'c' ]

)*/

/** 10.promise.resolve方法的實現(xiàn) **/
/*MyPromise.resolve(100).then(value => console.log(value))
MyPromise.resolve(p1()).then(value => console.log(value))*/

/** 10.promise.finally方法的實現(xiàn)  **/
/*p2().finally(()=>{
    console.log('finally')
    return p1()
}).then(value=>{
    console.log(value)  // p2
},reason=>{
    console.log(reason)
})*/

/** 11.promise.catch方法的實現(xiàn) **/
/*p2().then(value => console.log(value))
    .catch(reason => console.log(reason))*/
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。