成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Promise 的then 里發(fā)生了什么

Joyven / 2601人閱讀

摘要:大家都知道怎么用,但是對于內(nèi)部的原理很多人都不是很清楚來看一個面試題的是怎么實(shí)現(xiàn)的首先來分析一下是屬于實(shí)例上的方法參數(shù)有個,分別為,并且都是可選的可以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用執(zhí)行要執(zhí)行或者方法參數(shù),分別有自己的參數(shù),分別是的參數(shù)跟的參數(shù)只能使用前一個的

Promise 大家都知道怎么用, 但是對于內(nèi)部的原理很多人都不是很清楚

來看一個面試題: Promise的then 是怎么實(shí)現(xiàn)的

首先來分析一下then

then是屬于實(shí)例上的方法

參數(shù)有2個,分別為onFulfilled, onRejected,并且都是可選的

可以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用

then執(zhí)行要執(zhí)行Promise onFulfilled 或者 onRejected 方法

參數(shù)onFulfilled,onRejected 分別有自己的參數(shù), 分別是resolve的參數(shù)跟reject的參數(shù)

then只能使用前一個then的返回值

then返回值不能是同一個promise

來一個一個看吧

then是屬于實(shí)例上的方法

Promise.prototype.then = function(){}

參數(shù)有2個,分別為onFulfilled, onRejected,并且都是可選的

Promise.prototype.then = function(onFulfilled,onRejected){}

可以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用

Promise.prototype.then = function(onFulfilled,onRejected){
    return new Promise(function(resolve,reject){
        // 代碼省略
    })
}

要實(shí)現(xiàn)promise鏈?zhǔn)? 返回的必須是一個Promise 再由于status 改變狀態(tài)了不能再變 所以需要第二個.... 第N個promise 調(diào)用新的resolve

then執(zhí)行要執(zhí)行Promise onFulfilled 或者 onRejected 方法

Promise.prototype.then = function(onFulfilled,onRejected){
    var self = this; // 保存當(dāng)前上下文
    return new Promise(function(resolve,reject){
        if(self.status === "resolved"){
             onFulfilled(self.res)
        }
        if(self.status === "rejected"){
            onRejected(self.err)
        }
    })
}

Promise.resolve(res) 、、 同步代碼的執(zhí)行情況下 上述邏輯成立

參數(shù)onFulfilled,onRejected 分別有自己的參數(shù), 分別是resolve的參數(shù)跟reject的參數(shù)

res 跟err 來源如下

function Promise(executor){
    let self = this
    this.status = "pending"  // Promise 的狀態(tài)值
    this.res = undefined   // 存成功之后的值
    this.err = undefined   // 存失敗之后的值
    executor(resolve,reject)
    // 省略代碼
}

executor 有2個參數(shù) 分別為resolve,reject
當(dāng)調(diào)用

new Promise((resolve,reject)=>{
    setTimeout(()=>{
        if(true){
            resolve("res")
        }else{
            reject("err")
        }
       
    },1000)
})

executor 會執(zhí)行 并且把成功的值或者失敗的值拋出來,resolve跟reject也是2個函數(shù) 定義在Promise內(nèi)部

function Promise(executor){
     // ...代碼省略
    function resolve(res){
        self.res = res 
    }
    function reject(err){
        self.err = err 
    }
    executor(resolve,reject)
    
}

resolve 跟reject 還需要改變 Promise的狀態(tài)值 并且一旦發(fā)生改變之后不能再次改變

function Promise(executor){
    // 代碼省略
    function resolve(res){
        if(self.status === "pending"){
            self.status = "resolved"
            self.res = res 
        }
    }
    function reject(err){
        if(self.status === "pending"){
            self.status = "rejected"
            self.err = err 
        }
    }
    executor(resolve,reject)
}

我們在executor中操作的往往是異步代碼, 這個之后直接then, status的狀態(tài)值并未發(fā)生改變, 所以不會執(zhí)行onFulfilled跟onRejected,
這個時候我們需要訂閱

function Promise(executor){
    // 代碼省略
    this.onFulfilledCallback = []  // 存成功之后的回調(diào)
    this.onrejectedCallback = []  // 存失敗之后的回調(diào)
    function resolve(res){
        if(self.status === "pending"){
            self.status = "resolved"
            self.res = res 
        }
    }
    function reject(err){
        if(self.status === "pending"){
            self.status = "rejected"
            self.err = err 
        }
    }
    executor(resolve,reject)
}

new Promise(executor).then((onFulfilled,onrejected)=>{
    var self = this; // 保存當(dāng)前上下文   **注意: 第二次調(diào)用then this是指向new Promise的**
    return new Promise(function(resolve,reject){
        // ...代碼省略
        if(self.status === "pending"){
            self.onFulfilledCallback.push(function(){   // 把成功之后需要做的事存起來  有多少個then就有多少個函數(shù)
                onFulfilled(self.res)   // 注意 這里的self.res 會隨著then的調(diào)用發(fā)生改變  因?yàn)槊看蝨hen都new 了一個Promise
            })
            self.onrejectedCallback.push(function(){   // 把失敗之后的事存起來 有多少個then就有多少個函數(shù)
                onFulfilled(self.err)
            })
        }
    })
})

當(dāng)resolve的時候 或者reject的時候
一一拿出來執(zhí)行

function resolve(res){
    // 省略代碼
    self.res = res
    onFulfilledCallback.forEach(fn=>{
        fn()
    })
}

實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用我們需要對then函數(shù)執(zhí)行的返回值做一個操作?。?!

需求:

then的參數(shù)onFulfilled函數(shù) 需要接收到上一個onfulfilled的執(zhí)行結(jié)果

Promise.prototype.then = function(onFulfilled,onRejected){
    return new Promise((resolve,reject)=>{
        // ...代碼省略
        if(self.status === "resolved"){
            let x = onFulfilled(self.res)   // 拿到onFulfilled的執(zhí)行結(jié)果  注意:這里執(zhí)行Promise.resolve() 同步代碼
            // 然后把x傳遞給下一個then
            resolve(x)
        }
    })
}

如果resolve是異步的話

Promise.prototype.then = function(onFulfilled,onRejected){
    return new Promise((resolve,reject)=>{
        // ...代碼省略
        if(self.status === "resolved"){
            let x = onFulfilled(self.res)   // 拿到onFulfilled的執(zhí)行結(jié)果  注意:這里執(zhí)行的是Promise.resolve() 同步代碼
            // 然后把x傳遞給下一個then
            resolve(x)
        }
        if(self.status === "pending"){
            self.onFulfilledCallback.push(function(){
                let x = onFulfilled(self.res)  // 這里的self.res 是上一個new Promise上的值 此時的onFUlfilled 相當(dāng)于 fn(){let x = onFulfilled}  
                resolve(x)
            })
        }
    })
}

//  同時為了拿到 fn(){let x = onFulfilled ...} 得值 傳遞給下一個onFulfilled,onFulfilledCallback需要改寫 onRejectedCallback同理

onFulfilledCallback.forEach(fn=>{
    return fn()
})
onRejectedCallback.forEach(fn=>{
    return fn()
})

-

onFullfillled 的返回值可能是個promise 也可能是個普通值

考慮如下情況:

1. 返回值是個promise

Promsie.resolve(11).then(res=>{

    return new Promise(executor) 
})

2. 返回值是個普通值

Promsie.resolve(11).then(res=>{

    return 1
})

最關(guān)鍵的來了
我們需要對onFullfillled 的返回值進(jìn)行判斷

修改then函數(shù)

// ...代碼省略
if(self.status === "resolved"){
        let x = onFulfilled(self.res)   // 拿到onFulfilled的執(zhí)行結(jié)果  注意:這里執(zhí)行的是Promise.resolve() 同步代碼
        // 然后把x傳遞給下一個then
        promiseResolve(x,resolve,reject)
        
    }
function promiseResolve(x,resolve,reject){
    if((typeof x != null && typeof x == "object") || typeof x == "function"){  // 確定是個引用類型
        if (x instanceof Promise){  // 確定是個Promise 實(shí)例
            let then = x.then
            then.call(x,res=>{    //  保證x調(diào)用res=>{}, res=>{}是then的onFulfilled方法  一直判斷直到onFulfilled 返回的是個普通值 然后resolve出去
                promiseResolve(res,resolve,reject)
            },err=>{
                reject(err)
            })
        }
    }else{
        resolve(x)
    }
}

then返回值不能是同一個promise

考慮這樣極端問題:

let p = new Promise(resolve=>{
    resolve(3333)
})

let p2 = p.then(resolve=>{
    return p2
})

這樣會出現(xiàn)什么問題呢, onFulfilled的返回結(jié)果如果是一個promise 上面的遞歸調(diào)用已經(jīng)說明了 我們要不斷的去遞歸最終的onFulfilled結(jié)果 然后再改變p2的status 這樣變成了p2去等待p2的執(zhí)行結(jié)果 函數(shù)死掉了

所以onFulfilled的返回結(jié)果需要跟then的返回結(jié)果去比較 修改函數(shù)

function promiseResolve(x,resolve,reject,promise){
    if(x === promise ){
        return new Error("引用錯誤!")
    }
    if((typeof x != null && typeof x == "object") || typeof x == "function"){  // 確定是個引用類型
        if (x instanceof Promise){  // 確定是個Promise 實(shí)例
            let then = x.then
            then.call(x,res=>{    //  保證x調(diào)用res=>{}, res=>{}是then的onFulfilled方法  一直判斷直到onFulfilled 返回的是個普通值 然后resolve出去
                promiseResolve(res,resolve,reject)
            },err=>{
                reject(err)
            })
        }
    }else{
        resolve(x)
    }
}
Promise.prototype.then = function(onFulfilled,onRejected){

    // ...代碼省略
    let promise2 = new Promise((resolve,reject)=>{
        if(self.status === "pending"){
            self.onFulfilledCallback.push(function(){
                let x = onFulfilled(self.res)  // 這里的self.res 是上一個new Promise上的值 此時的onFUlfilled 相當(dāng)于 fn(){let x = onFulfilled}  
                promiseResolve(x,resolve,reject,promise2)
            })
        }
    })
    return promise2
}

這段代碼還是有錯誤, 由于javascript主線程是單線程的, 所以在then里的promiseResolve是拿不到promise2的 所以我們需要開啟異步 使用定時器或者nextTick 這里我們用定時器

Promise.prototype.then = function(onFulfilled,onRejected){

    // ...代碼省略
    let promise2 = new Promise((resolve,reject)=>{
        if(self.status === "pending"){
            self.onFulfilledCallback.push(function(){
                setTimeout(()=>{
                    let x = onFulfilled(self.res)  // 這里的self.res 是上一個new Promise上的值 此時的onFUlfilled 相當(dāng)于 fn(){let x = onFulfilled}  
                    promiseResolve(x,resolve,reject,promise2)
                },0)
            })
        }
    })
    return promise2
}

此時 整個then差不多完成了

需要補(bǔ)充的點(diǎn)

onFulfilled,onRejected 有可能是undefined 這里未做判斷

在new Promise 時 executor函數(shù)內(nèi)部有可能報錯 這里未使用try catch捕獲

這里對于onRejected的錯誤未拋出

總結(jié)

then每次創(chuàng)建一個新的promise對象 對于同步的resolve,reject直接調(diào)用onFulfilled或者onRejected ,對于異步的resolve,reject使用
訂閱發(fā)布模式,把每個resolve,reject 暫存起來 等達(dá)到條件時候一一執(zhí)行, 并且拿到返回結(jié)果去跟內(nèi)部的promise比較,并且判斷如果是一個promise的話,不斷解析onFulfilled 的返回結(jié)果 直至resolve出去

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102006.html

相關(guān)文章

  • ES6 Promise - Promise生命周期和創(chuàng)建

    摘要:通過構(gòu)造函數(shù),可以創(chuàng)建一個。函數(shù)本身是一個異步行為,其方法的第三個參數(shù)為一個回調(diào)函數(shù),用來接收文件讀取的結(jié)果失敗時候的和成功時候的。第一個函數(shù)的參數(shù)對應(yīng)的參數(shù),第二個回調(diào)函數(shù)對應(yīng)的參數(shù)。 一:Promise的概念 Promise的中文意思是‘承諾’,什么叫承諾?承諾就是現(xiàn)在沒有發(fā)生,在將來的某個時刻一定會發(fā)生的事情。放在編程語言的環(huán)境下,Promise就是異步事件的結(jié)果的占位符。我們不...

    Hydrogen 評論0 收藏0
  • 原創(chuàng) | JS異步工具之--Promise

    摘要:作者珂珂滬江前端開發(fā)工程師本文為原創(chuàng)文章,有不當(dāng)之處歡迎指出。只對未來發(fā)生的事情做出兩種基本情況的應(yīng)對成功和失敗。在異步轉(zhuǎn)同步這條道路上,只是一個出彩的點(diǎn),他還尚有一些缺陷和不足,并不是我們最終的解決方案。 作者:珂珂 (滬江前端開發(fā)工程師)本文為原創(chuàng)文章,有不當(dāng)之處歡迎指出。轉(zhuǎn)載請標(biāo)明出處。 一個新事物的產(chǎn)生必然是有其歷史原因的。為了更好的以同步的方式寫異步的代碼,人們在JS上操碎了...

    alanoddsoff 評論0 收藏0
  • 馬蹄疾 | 詳解 JavaScript 異步機(jī)制及發(fā)展歷程(萬字長文)

    摘要:本文從入手,系統(tǒng)的回顧的異步機(jī)制及發(fā)展歷程。需要提醒的是,文本沒有討論的異步機(jī)制。這就是之前提到的事件觸發(fā)線程。其實(shí)無論是請求還是定時器還是事件,我們都可以統(tǒng)稱它們?yōu)槭录?。第二階段,引擎線程專注于處理事件。將外元素的事件回調(diào)放入調(diào)用棧。 functionshowImg(url){ varframeid=frameimg+Math.random(); window.img=window....

    shaonbean 評論0 收藏0
  • 寫一個符合promiseA+規(guī)范promise實(shí)現(xiàn)

    摘要:如何寫一個符合規(guī)范的實(shí)現(xiàn)前言是異步編程的一種解決方案從語法上講,是一個對象,從它可以獲取異步操作的消息從本意上講,它是承諾,承諾它過一段時間會給你一個結(jié)果。 如何寫一個符合promiseA+規(guī)范的promise實(shí)現(xiàn) 前言 Promise 是異步編程的一種解決方案:從語法上講,promise是一個對象,從它可以獲取異步操作的消息;從本意上講,它是承諾,承諾它過一段時間會給你一個結(jié)果。pr...

    hatlonely 評論0 收藏0
  • Promise初級與進(jìn)階---都在這

    摘要:處理和前一個回調(diào)函數(shù)運(yùn)行時發(fā)生的錯誤發(fā)生錯誤對象的錯誤具有冒泡性質(zhì),會一直向后傳遞,直到被捕獲為止。 0 前言 我一直以為我對Promise比較了解,相關(guān)的方法已經(jīng)非常熟悉了,直到我看到這篇文章,里面提出了這樣一個問題:Q: 假定 doSomething() 和 doSomethingElse() 均返回 promises,下面的四種 promises 的區(qū)別是什么 /...

    Ocean 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<