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

資訊專欄INFORMATION COLUMN

Build Your Own Promise

susheng / 3192人閱讀

摘要:意味著代指的操作由于某些原因失敗。第一步構(gòu)造函數(shù)有三種狀態(tài),。這個(gè)構(gòu)造函數(shù)我們可以先這樣寫(xiě)創(chuàng)建一個(gè)時(shí),首先進(jìn)行狀態(tài)初始化。所有的都是的,而并不是所有的對(duì)象都是。

一、JavaScript異步編程背景

? 從去年ES2015發(fā)布至今,已經(jīng)過(guò)去了一年多,ES2015發(fā)布的新的語(yǔ)言特性中最為流行的也就莫過(guò)于Promise了,Promise使得如今JavaScript異步編程如此輕松愜意,甚至慢慢遺忘了曾經(jīng)那不堪回首的痛楚。其實(shí)從JavaScript誕生,JavaScript中的異步編程就已經(jīng)出現(xiàn),例如點(diǎn)擊鼠標(biāo)、敲擊鍵盤(pán)這些事件的處理函數(shù)都是異步的,時(shí)間到了2009年,Node.js橫空出世,在整個(gè)Node.js的實(shí)現(xiàn)中,將回調(diào)模式的異步編程機(jī)制發(fā)揮的淋漓盡致,Node的流行也是的越來(lái)越多的JavaScripter開(kāi)始了異步編程,但是回調(diào)模式的副作用也慢慢展現(xiàn)在人們眼前,錯(cuò)誤處理不夠優(yōu)雅以及嵌套回調(diào)帶來(lái)的“回調(diào)地獄”。這些副作用使得人們從回調(diào)模式的溫柔鄉(xiāng)中慢慢清醒過(guò)來(lái),開(kāi)始尋找更為優(yōu)雅的異步編程模式,路漫漫其修遠(yuǎn)兮、吾將上下而求索。時(shí)間到了2015年,Promise拯救那些苦苦探索的先驅(qū)。行使它歷史使命的時(shí)代似乎已經(jīng)到來(lái)。

? 每個(gè)事物的誕生有他的歷史使命,更有其歷史成因,促進(jìn)其被那些探索的先驅(qū)們所發(fā)現(xiàn)。了解nodejs或者熟悉瀏覽器的人都知道,JavaScript引擎是基于事件循環(huán)或單線程這兩個(gè)特性的。更為甚者在瀏覽器中,更新UI(也就是瀏覽器重繪、重拍頁(yè)面布局)和執(zhí)行JavaScript代碼也在一個(gè)單線程中,可想而知,一個(gè)線程就相當(dāng)于只有一條馬路,如果一輛馬車(chē)拋錨在路上了阻塞了馬路,那么別的馬車(chē)也就擁堵在了那兒,這個(gè)單線程容易被阻塞是一個(gè)道理,單線程也只能允許某一時(shí)間點(diǎn)只能夠執(zhí)行一段代碼。同時(shí),JavaScript沒(méi)有想它的哥哥姐姐們那么財(cái)大氣粗,像Java或者C++,一個(gè)線程不夠,那么再加一個(gè)線程,這樣就能夠同時(shí)執(zhí)行多段代碼了,但是這樣就會(huì)帶來(lái)的隱患就是狀態(tài)不容易維護(hù),JavaScript選擇了單線程非阻塞式的方式,也就是異步編程的方式,就像上面的馬車(chē)拋錨在了路上,那么把馬車(chē)推到路邊的維修站,讓其他馬車(chē)先過(guò)去,等馬車(chē)修好了再回到馬路上繼續(xù)行駛,這就是單線程非阻塞方式。正如Promise的工作方式一樣,通過(guò)Promise去向服務(wù)器發(fā)起一個(gè)請(qǐng)求,畢竟請(qǐng)求有網(wǎng)絡(luò)開(kāi)銷(xiāo),不可能馬上就返回請(qǐng)求結(jié)果的,這個(gè)時(shí)候Promise就處于pending狀態(tài),但是其并不會(huì)阻塞其他代碼的執(zhí)行,當(dāng)請(qǐng)求返回時(shí),修改Promise狀態(tài)為fulfilled或者rejected(失敗請(qǐng)求)。同時(shí)執(zhí)行綁定到這兩個(gè)狀態(tài)上面的“處理函數(shù)”。這就是異步編程的模式,也就是Promise兢兢業(yè)業(yè)的工作方式,在下面一個(gè)部分將詳細(xì)討論P(yáng)romise。

二、Promise基礎(chǔ)

? 怎么一句話解釋Promise呢?Promise可以代指那些尚未完成的一些操作,但是其在未來(lái)的某個(gè)時(shí)間會(huì)返回某一特定的結(jié)果。

? 當(dāng)創(chuàng)建一個(gè)Promise實(shí)例后,其代表一個(gè)未知的值,在將來(lái)的某個(gè)時(shí)間會(huì)返回一個(gè)成功的返回值,或者失敗的返回值,我們可以為這些返回值添加處理函數(shù),當(dāng)值返回時(shí),處理函數(shù)被調(diào)用。Promise總是處于下面三種狀態(tài)之一:

pending: Promise的初始狀態(tài),也就是未被fulfilled或者rejected的狀態(tài)。

fulfilled: 意味著promise代指的操作已經(jīng)成功完成。

rejected:意味著promise代指的操作由于某些原因失敗。

一個(gè)處于pending狀態(tài)的promise可能由于某個(gè)成功返回值而發(fā)展為fulfilled狀態(tài),也有可能因?yàn)槟承╁e(cuò)誤而進(jìn)入rejected狀態(tài),無(wú)論是進(jìn)入fulfilled狀態(tài)或者rejected狀態(tài),綁定到這兩種狀態(tài)上面的處理函數(shù)就會(huì)被執(zhí)行。并且進(jìn)入fulfilled或者rejected狀態(tài)也就不能再返回pending狀態(tài)了。

?

三、邊學(xué)邊寫(xiě)

上面說(shuō)了那么多,其實(shí)都是鋪墊。接下來(lái)我們就開(kāi)始實(shí)現(xiàn)自己的Promise對(duì)象。go go go?。?!

第一步:Promise構(gòu)造函數(shù)

Promise有三種狀態(tài),pending、fulfilled、rejected。

const PENDING = "PENDING" // Promise 的 初始狀態(tài)
const FULFILLED = "FULFILLED" // Promise 成功返回后的狀態(tài)
const REJECTED = "REJECTED" // Promise 失敗后的狀態(tài)

有了三種狀態(tài)后,那么我們?cè)趺磩?chuàng)建一個(gè)Promise實(shí)例呢?

const promise = new Promise(executor) // 創(chuàng)建Promise的語(yǔ)法

通過(guò)上面生成promise語(yǔ)法我們知道,Promise實(shí)例是調(diào)用Promise構(gòu)造函數(shù)通過(guò)new操作符生成的。這個(gè)構(gòu)造函數(shù)我們可以先這樣寫(xiě):

class Promise {
    constructor(executor) {
        this.status = PENDING // 創(chuàng)建一個(gè)promise時(shí),首先進(jìn)行狀態(tài)初始化。pending
        this.result = undefined // result屬性用來(lái)緩存promise的返回結(jié)果,可以是成功的返回結(jié)果,或失敗的返回結(jié)果
    }
}

我們可以看到上面構(gòu)造函數(shù)接受的參數(shù)executor。它是一個(gè)函數(shù),并且接受其他兩個(gè)函數(shù)(resolve和reject)作為參數(shù),當(dāng)resolve函數(shù)調(diào)用后,promise的狀態(tài)轉(zhuǎn)化為fulfilled,并且執(zhí)行成功返回的處理函數(shù)(不用著急后面會(huì)說(shuō)到怎么添加處理函數(shù))。當(dāng)reject函數(shù)調(diào)用后,promise狀態(tài)轉(zhuǎn)化為rejected,并且執(zhí)行失敗返回的處理函數(shù)。

現(xiàn)在我們的代碼大概是這樣的:

class Promise {
    constructor(executor) {
        this.status = PENDING 
        this.result = undefined
        executor(data => resolveProvider(this, data), err => rejectProvider(this, err))
    }
}

function resolveProvider(promise, data) {
    if (promise.status !== PENDING) return false
    promise.status = FULFILLED
}
function rejectProvider(promise, data) {
    if (promise.status !== PENDING) return false
    promise.status = FULFILLED
}

Dont Repeat Yourselt?。?!我們可以看到上面代碼后面兩個(gè)函數(shù)基本相同,其實(shí)我們可以把它整合成一個(gè)函數(shù),在結(jié)合高階函數(shù)的使用。

const statusProvider = (promise, status) => data => {
    if (promise.status !== PENDING) return false
    promise.status = status
    promise.result = data
}
class Promise {
    constructor(executor) {
        this.status = PENDING 
        this.result = undefined
        executor(statusProvider(this, FULFILLED), statusProvider(this, REJECTED))
    }
}

現(xiàn)在我們的代碼就看上去簡(jiǎn)潔多了。

第二步:為Promise添加處理函數(shù)

其實(shí)通過(guò) new Promise(executor)已經(jīng)可以生成一個(gè)Promise實(shí)例了,甚至我們可以通過(guò)傳遞到executor中的resolve和reject方法來(lái)改變promise狀態(tài),但是!現(xiàn)在的promise依然沒(méi)啥卵用!??!因?yàn)槲覀儾](méi)有給它添加成功和失敗返回的處理函數(shù)。

首先我們需要給我們的promise增加兩個(gè)屬性,successListener和failureListener用來(lái)分別緩存成功處理函數(shù)和失敗處理函數(shù)。

class Promise {
    constructor(executor) {
        this.status = PENDING
         this.successListener = []
         this.failureListener = []
        this.result = undefined
        executor(statusProvider(this, FULFILLED), statusProvider(this, REJECTED))
    }
}

怎么添加處理函數(shù)呢?ECMASCRIPT標(biāo)準(zhǔn)中說(shuō)到,我們可以通過(guò)promise原型上面的then方法為promise添加成功處理函數(shù)和失敗處理函數(shù),可以通過(guò)catch方法為promise添加失敗處理函數(shù)。

const statusProvider = (promise, status) => data => {
    if (promise.status !== PENDING) return false
    promise.status = status
    promise.result = data
    switch(status) {
        case FULFILLED: return promise.successListener.forEach(fn => fn(data))
        case REJECTED: return promise.failurelistener.forEach(fn => fn(data))
    }
}
class Promise {
    constructor(executor) {
        this.status = PENDING
        this.successListener = []
        this.failurelistener = []
        this.result = undefined
        executor(statusProvider(this, FULFILLED), statusProvider(this, REJECTED))
    }
    /**
     * Promise原型上面的方法
     */
    then(...args) {
        switch (this.status) {
            case PENDING: {
                this.successListener.push(args[0])
                this.failurelistener.push(args[1])
                break
            }
            case FULFILLED: {
                args[0](this.result)
                break
            }
            case REJECTED: {
                args[1](this.result)
            }
        }
    }
    catch(arg) {
        return this.then(undefined, arg)
    }
}

我們現(xiàn)在的Promise基本初具雛形了。甚至可以運(yùn)用到一些簡(jiǎn)單的場(chǎng)景中了。舉個(gè)例子。

/*創(chuàng)建一個(gè)延時(shí)resolve的pormise*/
new Promise((resolve, reject) => {setTimeout(() => resolve(5), 2000)}).then(data => console.log(data)) // 5
/*創(chuàng)建一個(gè)及時(shí)resolve的promise*/
new Promise((resolve, reject) => resolve(5)).then(data => console.log(data)) // 5
/*鏈?zhǔn)秸{(diào)用then方法還不能夠使用!*/
new Promise(resolve=> resolve(5)).then(data => data).then(data => console.log(data))
// Uncaught TypeError: Cannot read property "then" of undefined
第三步:Promise的鏈?zhǔn)秸{(diào)用

Promise需要實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,我們需要再次回顧下then方法的定義:

then方法為pormise添加成功和失敗的處理函數(shù),同時(shí)then方法返回一個(gè)新的promise對(duì)象,這個(gè)新的promise對(duì)象resolve處理函數(shù)的返回值,或者當(dāng)沒(méi)有提供處理函數(shù)時(shí)直接resolve原始的值。

可以看出,promise能夠鏈?zhǔn)秸{(diào)用歸功于then方法返回一個(gè)全新的promise,并且resolve處理函數(shù)的返回值,當(dāng)然,如果then方法的處理函數(shù)本身就返回一個(gè)promise,那么久不用我們自己手動(dòng)生成一個(gè)promise了。了解了這些,就開(kāi)始動(dòng)手寫(xiě)代碼了。

const isPromise = object => object && object.then && typeof object.then === "function"
const noop = () => {}

const statusProvider = (promise, status) => data => {
    // 同上面代碼
}

class Promise {
    constructor(executor) {
        // 同上面代碼
    }
    then(...args) {
        const child = new this.constructor(noop)

        const handler = fn => data => {
            if (typeof fn === "function") {
                const result = fn(data)
                if (isPromise(result)) {
                    Object.assign(child, result)
                } else {
                    statusProvider(child, FULFILLED)(result)
                }    
            } else if(!fn) {
                statusProvider(child, this.status)(data)
            }
        }
        switch (this.status) {
            case PENDING: {
                this.successListener.push(handler(args[0]))
                this.failurelistener.push(handler(args[1]))
                break
            }
            case FULFILLED: {
                handler(args[0])(this.result)
                break
            }
            case REJECTED: {
                handler(args[1])(this.result)
                break
            }
        }
        return child
    }
    catch(arg) {
        return this.then(undefined, arg)
    }
}

? 首先我們寫(xiě)了一個(gè)isPromise方法,用于判斷一個(gè)對(duì)象是否是promise。就是判斷對(duì)象是否有一個(gè)then方法,免責(zé)聲明為了實(shí)現(xiàn)上的簡(jiǎn)單,我們不區(qū)分thenable和promise的區(qū)別,但是我們應(yīng)該是知道。所有的promise都是thenable的,而并不是所有的thenable對(duì)象都是promise。(thenable對(duì)象是指帶有一個(gè)then方法的對(duì)象,該then方法其實(shí)就是一個(gè)executor。)isPromise的作用就是用于判斷then方法返回值是否是一個(gè)promise,如果是promise,就直接返回該promise,如果不是,就新生成一個(gè)promise并返回該promise。

? 由于需要鏈?zhǔn)秸{(diào)用,我們對(duì)successListener和failureListener中處理函數(shù)進(jìn)行了重寫(xiě),并不是直接push進(jìn)去then方法接受的參數(shù)函數(shù)了,因?yàn)閠hen方法需要返回一個(gè)promise,所以當(dāng)then方法里面的處理函數(shù)被執(zhí)行的同時(shí),我們也需要對(duì)then方法返回的這個(gè)promise進(jìn)行處理,要么resolve,要么reject掉。當(dāng)然,大部分情況都是需要resolve掉的,只有當(dāng)then方法沒(méi)有添加第二個(gè)參數(shù)函數(shù),同時(shí)調(diào)用then方法的promise就是rejected的時(shí)候,才需要把then方法返回的pormise進(jìn)行reject處理,也就是調(diào)用statusProvider(child, REJECTED)(data).

toy Promise實(shí)現(xiàn)的完整代碼:

const PENDING = "PENDING" // Promise 的 初始狀態(tài)
const FULFILLED = "FULFILLED" // Promise 成功返回后的狀態(tài)
const REJECTED = "REJECTED" // Promise 失敗后的狀態(tài)

const isPromise = object => object && object.then && typeof object.then === "function"
const noop = () => {}

const statusProvider = (promise, status) => data => {
    if (promise.status !== PENDING) return false
    promise.status = status
    promise.result = data
    switch(status) {
        case FULFILLED: return promise.successListener.forEach(fn => fn(data))
        case REJECTED: return promise.failurelistener.forEach(fn => fn(data))
    }
}

class Promise {
    constructor(executor) {
        this.status = PENDING
        this.successListener = []
        this.failurelistener = []
        this.result = undefined 
        executor(statusProvider(this, FULFILLED), statusProvider(this, REJECTED))
    }
    /**
     * Promise原型上面的方法
     */
    then(...args) {
        const child = new this.constructor(noop)

        const handler = fn => data => {
            if (typeof fn === "function") {
                const result = fn(data)
                if (isPromise(result)) {
                    Object.assign(child, result)
                } else {
                    statusProvider(child, FULFILLED)(result)
                }    
            } else if(!fn) {
                statusProvider(child, this.status)(data)
            }
        }
        switch (this.status) {
            case PENDING: {
                this.successListener.push(handler(args[0]))
                this.failurelistener.push(handler(args[1]))
                break
            }
            case FULFILLED: {
                handler(args[0])(this.result)
                break
            }
            case REJECTED: {
                handler(args[1])(this.result)
                break
            }
        }
        return child
    }
    catch(arg) {
        return this.then(undefined, arg)
    }
}
四、怎么讓我們的toy Promise變強(qiáng)健

在ECMAScript標(biāo)準(zhǔn)中,Promise構(gòu)造函數(shù)上面還提供了一些靜態(tài)方法,比如Promise.resolve、Promise.rejectPromsie.all、Promise.race。當(dāng)我們有了上面的基礎(chǔ)實(shí)現(xiàn)后,為我們的toy Promise添加上面這些新的功能一定能讓其更加實(shí)用。

在我們的基本實(shí)現(xiàn)中,我們并沒(méi)有區(qū)分thenable對(duì)象,其實(shí)Promise.resolvethen方法都可以接受一個(gè)thenable對(duì)象,并把該thenable對(duì)象轉(zhuǎn)化為一個(gè)promise對(duì)象,如果想讓我們的toy Promise用于生產(chǎn)的話,這也是要考慮的。

為了讓我們的toy Promise變得更強(qiáng)壯,我們需要擁有強(qiáng)健的錯(cuò)誤處理機(jī)制,比如驗(yàn)證executor必須是一個(gè)函數(shù)、then方法的參數(shù)只能是函數(shù)或者undefined或null,又比如executor和then方法中拋出的錯(cuò)誤并不能夠被window.onerror監(jiān)測(cè)到,而只能夠通過(guò)錯(cuò)誤處理函數(shù)來(lái)處理,這也是需要考慮的因素。

如果我們的Promise polyfill是考慮支持多平臺(tái),那么首要考慮的就是瀏覽器環(huán)境或Node.js環(huán)境,其實(shí)在這兩個(gè)平臺(tái),原生Promise都是支持兩個(gè)事件的。就拿瀏覽器端舉例:

unhandledrejection: 在一個(gè)事件循環(huán)中,如果我們沒(méi)有對(duì)promise返回的錯(cuò)誤進(jìn)行處理,那么就會(huì)在window對(duì)象上面觸發(fā)該事件。

rejectionhandled:如果在一個(gè)事件循環(huán)后,我們才去對(duì)promise返回的錯(cuò)誤進(jìn)行處理,那么就會(huì)在window對(duì)象上面監(jiān)聽(tīng)到此事件。

關(guān)于這兩個(gè)事件以及node.js平臺(tái)上面類似的事件請(qǐng)參考Nicholas C. Zakas新書(shū)

Promise能夠很棒的處理異步編程,要想學(xué)好它我認(rèn)為最好的方法就是親自動(dòng)手去實(shí)現(xiàn)一個(gè)自己的Promise,下面的項(xiàng)目Jocs/promise是我的實(shí)現(xiàn),歡迎大家pr和star。

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

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

相關(guān)文章

  • 使用 Forge Viewer 在序列中聚合多模型

    摘要:但模型載入程序并不是同步執(zhí)行的載入文檔和幾何等動(dòng)作在里都是異步的,我們沒(méi)辦法知道哪一個(gè)模型是第一個(gè)被完整載入,和下個(gè)一個(gè)完全載入的是誰(shuí)而在一些應(yīng)用場(chǎng)景里是有可能需要在一個(gè)序列聚合多個(gè)模型。 showImg(https://segmentfault.com/img/bVVaPI?w=600&h=390); 此篇博客原著為 Autodesk ADN 的梁曉冬,以下以我簡(jiǎn)稱。 我的同事創(chuàng)作了...

    graf 評(píng)論0 收藏0
  • Build your own AngularJS》筆記分享

    摘要:初次寫(xiě)文章,請(qǐng)多多包涵我最近正在根據(jù)這本書(shū)從頭開(kāi)始實(shí)現(xiàn)了一遍的框架。筆記目錄鏈接個(gè)人認(rèn)為本書(shū)對(duì)于想了解框架源碼的讀者來(lái)說(shuō)相當(dāng)有用,完全值得去購(gòu)買(mǎi)這本書(shū)書(shū)本主頁(yè)。因?yàn)槭浅鯇W(xué)者,筆記里可能有一些錯(cuò)誤,我也會(huì)繼續(xù)修改。 (初次寫(xiě)文章,請(qǐng)多多包涵) 我最近正在根據(jù)《Build your own angularJS》這本書(shū)從頭開(kāi)始實(shí)現(xiàn)了一遍AngularJS的框架。我把相關(guān)的源碼和我的個(gè)人學(xué)習(xí)筆...

    firim 評(píng)論0 收藏0
  • Build your own AngularJS》手記

    摘要:最近在看,打算跟著書(shū)中的代碼敲一遍,加深對(duì)的理解。在這里記錄過(guò)程中的問(wèn)題與心得。根據(jù)排查內(nèi)存耗盡應(yīng)該是這個(gè)版本的問(wèn)題,換成后問(wèn)題消失。因此認(rèn)為這種寫(xiě)法是有風(fēng)險(xiǎn)的,必須用頂上那一行注釋表明我確實(shí)要全局都的才行。不得不感嘆的嚴(yán)謹(jǐn)。 最近在看 build your own angularjs ,打算跟著書(shū)中的代碼敲一遍,加深對(duì)AngularJS的理解。在這里記錄過(guò)程中的問(wèn)題與心得。 Int...

    zsy888 評(píng)論0 收藏0
  • build your own xxx】實(shí)現(xiàn)你自己的call和apply

    摘要:新開(kāi)一個(gè)坑,起名為,自己造一些小輪子。之前貌似在知乎上看到一個(gè)問(wèn)題是說(shuō)如何使用實(shí)現(xiàn)它原生的和方法,今天我來(lái)實(shí)現(xiàn)一番。但是,這樣等于說(shuō)只給傳了一個(gè)數(shù)組參數(shù),并不能達(dá)到目的。同理來(lái)實(shí)現(xiàn)參考資料深入之和的模擬實(shí)現(xiàn) showImg(https://segmentfault.com/img/bVbbHCv?w=1123&h=629); 新開(kāi)一個(gè)坑,起名為【build your xxx】,自己造一...

    qpal 評(píng)論0 收藏0
  • build your own xxx】實(shí)現(xiàn)你自己的bind函數(shù)

    今天來(lái)實(shí)現(xiàn)JavaScript的bind函數(shù)。首先看MDN的bind函數(shù)描述: 從上面可以看出來(lái),var A = B.bind(this)函數(shù)其實(shí)干了這幾件事情: 返回一個(gè)函數(shù),且這個(gè)函數(shù)后面運(yùn)行時(shí)的this就是bind(this)傳入的this 接收參數(shù),這些參數(shù)(如果有的話)作為bind()的第二個(gè)參數(shù)跟在this(或其他對(duì)象)后面,之后它們會(huì)被插入到目標(biāo)函數(shù)的參數(shù)列表的開(kāi)始位置,傳遞給綁定...

    dantezhao 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<