国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

啥?喝著闊落吃著西瓜就把Promise手寫出來了???

idisfkj / 3578人閱讀

摘要:嗝首先,我們通過字面可以看出來是一種解決方案,而且還有兩種傳統的解決方案回調函數和事件,,那么我們就來先聊聊這兩種方案。

前言

雖然今年已經18年,但是今天還是要繼續聊聊ES6的東西,ES6已經過去幾年,可是我們對于ES6的語法究竟是掌握了什么程度,是了解?會用?還是精通?相信大家和我一樣都對自己有著一個提升的心,對于新玩具可不能僅僅了解,對于其中的思想才是最吸引人的,所以接下來會通過一篇文章,來讓大家對于Promise這個玩具做到精通的程度!!!

打開一瓶冰闊落~~~

Promise
Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最早提出和實現,ES6將其寫進了語言標準,統一了用法,原生提供了Promise對象。

~

首先,我們通過字面可以看出來Pormise是一種解決方案,而且還有兩種傳統的解決方案·回調函數事件,ok,那么我們就來先聊聊這兩種方案。

回調函數 Callback

回調函數想必大家都不陌生,就是我們常見的把一個函數當做參數傳遞給另外一個函數,在滿足了一定的條件之后再去執行回調,比如我們想要實現一個在三秒后去計算1到5的和,那么:

    // 求和函數
    function sum () {
        return eval([...arguments].join("+"))
    }
    // 三秒后執行函數
    function asycnGetSum (callback) {
        setTimeout(function(){
            var result = callback(1,2,3,4,5);
            console.log(result)
        },3000)
    }
    asyncGetSum(sum);

這樣的實現就是回調函數,但是如果我要實現在一段動畫,動畫的執行過程是小球先向右移動100px,然后再向下移動100px,在向左移動100px,每段動畫持續時間都是3s.

    dom.animate({left:"100px"},3000,"linear",function(){
        dom.animate({top:"100px"},3000,"linear",function(){
            dom.animate({left:"0px"},3000,"linear",function(){
                console.log("動畫 done")
            })
        })
    })

這樣就會看到形成了一個回調嵌套,也就是我們常說的回調地獄,導致代碼可讀性十分差。

事件

事件處理就是jQuery中的on綁定事件和trigger觸發事件,其實就是我們常見的發布訂閱模式,當我訂閱了一個事件,那么我就是訂閱者,如果發布者發布了數據之后,那么我就要收到相應的通知。

    // 定義一個發布中心
    let publishCenter = {
        subscribeArrays:{}, // 定義一個訂閱者回調函數callback
        subscribe:function(key,callback){
            // 增加訂閱者
            if(!this.subscribeArrays[key]){
                this.subscribeArrays[key] = [];
            }
            this.subscribeArrays[key].push(callback)
        },
        publish:function(){
            //發布 第一個參數是key
            let params = [...arguments];
            let key = params.shift();
            let callbacks = this.subscribeArrays[key];
            if(!callbacks || callbacks.length === 0){
                // 如果沒人訂閱 那么就返回
                return false
            }
            for( let i = 0 ; i < callbacks.length; i++ ){
                callbacks[i].apply( this, params );
            }
        }
    };
    
    // 訂閱 一個wantWatermelon事件
    publishCenter.subscribe("wantWatermelon",function(){console.log("恰西瓜咯~~")})
    
    //觸發wantWatermelon事件 好咯 可以看到 恰西瓜咯
    publishCenter.publish("wantWatermelon")

恰西瓜中~~~

Promise A+

嗝~ok,吃完我們進入正題,看到上面異步編程如此如此如此麻煩,對于我這種頭大用戶,當然是拒絕的啊,還好我們有PormisePormise大法好),下面我們就來通過實現一個Promise去更深的了解Promise的原理,首先我們了解一下PromiseA+,它是一種規范,用來約束大家寫的Promise方法的,為了讓大家寫的Promise杜絕一些錯誤,按照我們所期望的流程來走,因此就出現了PromiseA+規范。

Promise特點

我們根據PromiseA+文檔來一步一步的看Promise有什么特點。

首先我們看文檔的2.1節,題目是Promise states,也就是說講的是Promise的狀態,那么都說了些什么呢,我們來看一哈:

一個promise只有三種狀態,pending態,fulfilled態(完成態),rejected(拒絕態)

當promise處于pending態時,可能轉化成fulfilled或者rejected

一旦promise的狀態改成了fulfilled后,狀態就不能再改變了,并且需要提供一個不可變的value

一旦promise的狀態改成了rejected后,狀態就不能再改變了,并且需要提供一個不可變的reason

ok,那么我們就開始寫我們自己的Promise,我們先看看一段正常Promise的寫法

    // 成功或者失敗是需要提供一個value或者reason
    let promise1 = new Promise((resolve,rejected)=>{
        // 可以發現 當我們new Promise的時候這句話是同步執行的 也就是說當我們初始化一個promise的時候 內部的回調函數(通常我們叫做執行器executor)會立即執行
        console.log("hahahha");
        // promise內部支持異步
        setTimeout(function(){
            resolve(123);
        },100)
        // throw new Error("error") 我們也可以在執行器內部直接拋出一個錯誤 這時promise會直接變成rejected態
    })
    

根據我們上面的代碼還有PromiseA+規范中的狀態說明,我們可以知道Promise已經有了下面幾個特點

promise有三種狀態 默認pendingpending可以變成fulfilled(成功態)或者rejected(失敗態),而一旦轉變之后就不能在變成其他值了

promise內部有一個value 用來存儲成功態的結果

promise內部有一個reason 用來存儲失敗態的原因

promise接受一個executor函數,這個函數有兩個參數,一個是resolve方法,一個是reject方法,當執行resolve時,promise狀態改變為fulfilled,執行reject時,promise狀態改變為rejected

默認 new Promise 執行的時候內部的executor函數執行

promise內部支持異步改變狀態

promise內部支持拋出異常,那么該promise的狀態直接改成rejected

我們接下來繼續看PromiseA+文檔:

promise必須要有一個then方法,用來訪問它當前的value或者是reason

該方法接受兩個參數onFulfilled(成功回掉函數),onRejected(失敗回調函數) promise.then(onFulfilled, onRejected)

這兩個參數都是可選參數,如果發現這兩個參數不是函數類型的話,那么就忽略 比如 promise.then().then(data=>console.log(data),err=>console.log(err)) 就可以形成一個值穿透

onFulfilled必須在promise狀態改成fulfilled之后改成調用,并且呢promise內部的value值是這個函數的參數,而且這個函數不能重復調用

onRejected必須在promise狀態改成rejected之后改成調用,并且呢promise內部的reason值是這個函數的參數,而且這個函數不能重復調用

onFulfilledonRejected這兩個方法必須要在當前執行棧的上下文執行完畢后再調用,其實就是事件循環中的微任務(setTimeout是宏任務,有一定的差異)

onFulfilledonRejected這兩個方法必須通過函數調用,也就是說 他們倆不是通過this.onFulfilled()或者this.onRejected()調用,直接onFulfilled()或者onRejected()

then方法可以在一個promise上多次調用,也就是我們常見的鏈式調用

如果當前promise的狀態改成了fulfilled那么就要按照順序依次執行then方法中的onFulfilled回調

如果當前promise的狀態改成了rejected那么就要按照順序依次執行then方法中的onRejected回調

then方法必須返回一個promise(接下來我們會把這個promise稱做promise2),類似于 promise2 = promise1.then(onFulfilled, onRejected);

如果呢onFulfilled()或者onRejected()任一一個返回一個值x,那么就要去執行resolvePromise這個函數中去(這個函數是用來處理返回值x遇到的各種值,然后根據這些值去決定我們剛剛then方法中onFulfilled()或者onRejected()這兩個回調返回的promise2的狀態)

如果我們在then中執行onFulfilled()或者onRejected()方法時產生了異常,那么就將promise2用異常的原因ereject

如果onFulfilled或者onRejected不是函數,并且promise的狀態已經改成了fulfilled或者rejected,那么就用同樣的value或者reason去更新promise2的狀態(其實這一條和第三條一個道理,也就是值得穿透問題)

好吧,我們總結了這么多規范特點,那么我們就用這些先來練練手

    /**
     * 實現一個PromiseA+
     * @description 實現一個簡要的promise
     * @param {Function} executor 執行器
     * @author Leslie
     */
    function Promise(executor){
        let self = this;
        self.status = "pending"; // 存儲promise狀態 pending fulfilled rejected.
        self.value = undefined; // 存儲成功后的值
        self.reason = undefined; // 記錄失敗的原因
        self.onfulfilledCallbacks = []; //  異步時候收集成功回調
        self.onrejectedCallbacks = []; //  異步時候收集失敗回調
        function resolve(value){
            if(self.status === "pending"){
                self.status = "fulfilled";// resolve的時候改變promise的狀態
                self.value = value;//修改成功的值
                // 異步執行后 調用resolve 再把存儲的then中的成功回調函數執行一遍
                self.onfulfilledCallbacks.forEach(element => {
                    element()
                });
            }
        }
        function reject(reason){
            if(self.status === "pending"){
                self.status = "rejected";// reject的時候改變promise的狀態
                self.reason = reason; // 修改失敗的原因
                // 異步執行后 調用reject 再把存儲的then中的失敗回調函數執行一遍
                self.onrejectedCallbacks.forEach(element => {
                    element()
                });
            }
        }
        // 如果執行器中拋出異常 那么就把promise的狀態用這個異常reject掉
        try {
            //執行 執行器
            executor(resolve,reject);
        } catch (error) {
            reject(error)
        }
    }

    Promise.prototype.then = function(onfulfilled,onrejected){
        // onfulfilled then方法中的成功回調
        // onrejected then方法中的失敗回調
        let self = this;
        // 如果onfulfilled不是函數 那么就用默認的函數替代 以便達到值穿透
        onfulfilled = typeof onfulfilled === "function"?onfulfilled:val=>val;
        // 如果onrejected不是函數 那么就用默認的函數替代 以便達到值穿透
        onrejected = typeof onrejected === "function"?onrejected: err=>{throw err}
        let promise2 = new Promise((resolve,reject)=>{
            if(self.status === "fulfilled"){
                // 加入setTimeout 模擬異步
                // 如果調用then的時候promise 的狀態已經變成了fulfilled 那么就調用成功回調 并且傳遞參數為 成功的value
                setTimeout(function(){
                    // 如果執行回調發生了異常 那么就用這個異常作為promise2的失敗原因
                    try {
                        // x 是執行成功回調的結果
                        let x = onfulfilled(self.value);
                        // 調用resolvePromise函數 根據x的值 來決定promise2的狀態
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (error) {
                        reject(error)
                    }
                },0)
                
            }
        
            if(self.status === "rejected"){
                // 加入setTimeout 模擬異步
                // 如果調用then的時候promise 的狀態已經變成了rejected 那么就調用失敗回調 并且傳遞參數為 失敗的reason
                setTimeout(function(){
                    // 如果執行回調發生了異常 那么就用這個異常作為promise2的失敗原因
                    try {
                        // x 是執行失敗回調的結果
                        let x = onrejected(self.reason);
                         // 調用resolvePromise函數 根據x的值 來決定promise2的狀態
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (error) {
                        reject(error)
                    }
                    
                },0)
            }
        
            if(self.status === "pending"){
                //如果調用then的時候promise的狀態還是pending,說明promsie執行器內部的resolve或者reject是異步執行的,那么就需要先把then方法中的成功回調和失敗回調存儲襲來,等待promise的狀態改成fulfilled或者rejected時候再按順序執行相關回調
                self.onfulfilledCallbacks.push(()=>{
                    //setTimeout模擬異步
                    setTimeout(function(){
                        // 如果執行回調發生了異常 那么就用這個異常作為promise2的失敗原因
                        try {
                             // x 是執行成功回調的結果
                            let x = onfulfilled(self.value)
                            // 調用resolvePromise函數 根據x的值 來決定promise2的狀態
                            resolvePromise(promise2,x,resolve,reject);
                        } catch (error) {
                            reject(error)
                        }
                    },0)
                })
                self.onrejectedCallbacks.push(()=>{
                    //setTimeout模擬異步
                    setTimeout(function(){
                        // 如果執行回調發生了異常 那么就用這個異常作為promise2的失敗原因
                        try {
                             // x 是執行失敗回調的結果
                            let x = onrejected(self.reason)
                             // 調用resolvePromise函數 根據x的值 來決定promise2的狀態
                            resolvePromise(promise2,x,resolve,reject);
                        } catch (error) {
                            reject(error)
                        }
                    },0)
                })
            }
        })
        return promise2;
    }

一氣呵成,是不是覺得之前總結出的特點十分有效,對著特點十分順暢的就擼完了代碼~

那么就讓我們接著來看看promiseA+文檔里還有些什么內容吧

resolvePromise這個函數呢會決定promise2用什么樣的狀態,如果x是一個普通值,那么就直接采用x,如果x是一個promise那么就將這個promise的狀態當成是promise2的狀態

判斷如果xpromise2是一個對象,即promise2 === x,那么就陷入了循環調用,這時候promise2就會以一個TypeErrorreason轉化為rejected

如果x是一個promise,那么promise2就采用x的狀態,用和x相同的valueresolve,或者用和x相同的reasonreject

如果x是一個對象或者是函數 那么就先執行let then = x.then

如果x不是一個對象或者函數 那么就resolve 這個x

如果在執行上面的語句中報錯了,那么就用這個錯誤原因去reject promise2

如果then是一個函數,那么就執行then.call(x,resolveCallback,rejectCallback)

如果then不是一個函數,那么就resolve這個x

如果xfulfilled態 那么就會走resolveCallback這個函數,這時候就默認把成功的value作為參數y傳遞給resolveCallback,即y=>resolvePromise(promise2,y),繼續調用resolvePromise這個函數 確保 返回值是一個普通值而不是promise

如果xrejected態 那么就把這個失敗的原因reason作為promise2的失敗原因reject出去

如果resolveCallback,rejectCallback這兩個函數已經被調用了,或者多次被相同的參數調用,那么就確保只調第一次,剩下的都忽略掉

如果調用then拋出異常了,并且如果resolveCallback,rejectCallback這兩個函數已經被調用了,那么就忽略這個異常,否則就用這個異常作為promise2reject原因

我們又又又又又又總結了這么多,好吧不說了總結多少就開擼吧。

/**
 * 用來處理then方法返回結果包裝成promise 方便鏈式調用
 * @param {*} promise2 then方法執行產生的promise 方便鏈式調用
 * @param {*} x then方法執行完成功回調或者失敗回調后的result
 * @param {*} resolve 返回的promise的resolve方法 用來更改promise最后的狀態
 * @param {*} reject 返回的promise的reject方法 用來更改promise最后的狀態
 */
function resolvePromise(promise2,x,resolve,reject){
    // 首先判斷x和promise2是否是同一引用 如果是 那么就用一個類型錯誤作為Promise2的失敗原因reject
    if( promise2 === x) return reject(new TypeError("typeError:大佬,你循環引用了!"));
    // called 用來記錄promise2的狀態改變,一旦發生改變了 就不允許 再改成其他狀態
    let called;
    if( x !== null && ( typeof x === "object" || typeof x === "function")){
        // 如果x是一個對象或者函數 那么他就有可能是promise 需要注意 null typeof也是 object 所以需要排除掉
        //先獲得x中的then 如果這一步發生異常了,那么就直接把異常原因reject掉
        try {
            let then = x.then;//防止別人瞎寫報錯
            if(typeof then === "function"){
                //如果then是個函數 那么就調用then 并且把成功回調和失敗回調傳進去,如果x是一個promise 并且最終狀態時成功,那么就會執行成功的回調,如果失敗就會執行失敗的回調如果失敗了,就把失敗的原因reject出去,做為promise2的失敗原因,如果成功了那么成功的value時y,這個y有可能仍然是promise,所以需要遞歸調用resolvePromise這個方法 直達返回值不是一個promise
                then.call(x,y => {
                    if(called) return;
                    called = true;
                    resolvePromise(promise2,y,resolve,reject)
                }, error=>{
                    if(called) return
                    called = true;
                    reject(error)
                })
            }else{
                resolve(x)
            }
        } catch (error) {
            if(called) return
            called = true;
            reject(error)
        }
    }else{
        // 如果是一個普通值 那么就直接把x作為promise2的成功value resolve掉
        resolve(x)
    }

}

finnnnnnnnnally,我們終于通過我們的不懈努力實現了一個基于PromiseA+規范的Promise!

最后呢為了完美,我們還要在這個promise上實現Promise.resolve,Promise.reject,以及catch,Promise.allPromise.race這些方法。

Promise的一些方法
Promise.resolve = function(value){
    return new Promise((resolve,reject)=>{
        resolve(value)
    })
}
Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason)
    })
}
Promise.prototype.catch = function(onRejected){
    return this.then(null,onRejected)
}
Promise.all = function(promises){
    return new Promise((resolve,reject)=>{
        let arr = [];
        let i = 0;
        function getResult(index,value){
            arr[index] = value;
            if(++i == promises.length) {
                resolve(arr)
            }
        }
        for(let i = 0;i{
                getResult(i,data)
            },reject)
        }
    })
}
Promise.race = function(promises){
    return new Promise((resolve,reject)=>{
        for(let i = 0 ; i < promises.length ; i++){
            promises[i].then(resolve,reject)
        }
    })
}
Promise 語法糖

恰完西瓜來口糖,語法糖是為了讓我們書寫promise的時候能夠更加的快速,所以做了一層改變,我們來看一個例子,比如當我們封裝一個異步讀取圖片的寬高函數

    // 原來的方式
    let getImgWidthHeight = function(imgUrl){
        return new Promise((resolve,reject)=>{
            let img = new Image();
            img.onload = function(){
                resolve(img.width+"-"+img.height)
            }
            img.onerror = function(e){
                reject(e)
            }
            img.src = imgUrl;
        })
    }

是不是覺得怎么寫起來有點舒服但又有點不舒服,好像我每次都要去寫執行器??!為什么!好的,沒有為什么,既然不舒服 我們就改!

// 實現一個promise的語法糖
Promise.defer = Promise.deferred = function (){
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd
}

有了上面的語法糖我們再看一下那個圖片的函數怎么寫

    let newGetImgWidthHeight = function(imgUrl){
        let dfd = Promise.defer();
        let img = new Image();
        img.onload = function(){
            dfd.resolve(img.width+"-"+img.height)
        }
        img.onerror = function(e){
            dfd.reject(e)
        }
        img.url = imgUrl;
        return dfd.promise
    }

是不是發現我們少了一層函數嵌套,呼 得勁

最終檢測
npm install promises-aplus-tests -g

既然我們都說了我們是遵循promiseA+規范的,那至少要拿出點證據來是不是,不然是不是說服不了大家,那么我們就用promises-aplus-tests這個包來檢測我們寫的promise究竟怎么樣呢!安裝完成之后來跑一下我們的promise

最終跑出來我們全部通過測試!酷!晚餐再加個雞腿~

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96324.html

相關文章

  • Promise快速入門

    摘要:周五就想寫這篇文章,但是無奈花花世界的誘惑太多就一直拖到了今天,自責遍進入正題對象用于表示一個異步操作的最終狀態完成或失敗,以及其返回的值。 周五就想寫這篇文章,但是無奈花花世界的誘惑太多……就一直拖到了今天,自責1e4遍;進入正題Promise: Promise 對象用于表示一個異步操作的最終狀態(完成或失?。约捌浞祷氐闹怠?上為MDNPromise的定義;ES6規定Promis...

    bergwhite 評論0 收藏0
  • JS手寫bind之處理new的情況詳解

      你有遇見過給bind返回的函數做new操作的場景,本篇主要講述的就是實現一下兼容new操作的bind寫法,順便學習一下new操作符,為大家提供下參考?! 〈蠹铱梢匀タ聪玛P于 JS 中 bind 方法的實現的文章,并給出了實現:  Function.prototype.myBind=function(thisArg,...prefixArgs){   constfn=this;   return...

    3403771864 評論0 收藏0
  • 只會用就out,手寫一個符合規范的Promise

    摘要:傳入的回調函數也不是一個函數類型,那怎么辦規范中說忽略它就好了。因此需要判斷一下回調函數的類型,如果明確是個函數再執行它。 Promise是什么 所謂Promise,簡單說就是一個容器,里面保存著某個未來才會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處...

    muzhuyu 評論0 收藏0
  • 2019-我的前端面試題

    摘要:先說下我面試情況,我一共面試了家公司。篇在我面試的眾多公司里,只有同城的面問到相關問題,其他公司壓根沒問。我自己回答的是自己開發組件面臨的問題。完全不用擔心對方到時候打電話核對的問題。 2019的5月9號,離發工資還有1天的時候,我的領導親切把我叫到辦公室跟我說:阿郭,我們公司要倒閉了,錢是沒有的啦,為了不耽誤你,你趕緊出去找工作吧。聽到這話,我虎軀一震,這已經是第2個月沒工資了。 公...

    iKcamp 評論0 收藏0

發表評論

0條評論

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