手写 Promise 系列 --- 1

Promise 是 ES6 推出的用于解决callback嵌套层级太深问题的一种异步方案.

基本使用如下.

let myPromise = new Promise((resolve,reject) => {
    setTimeout(()=>{
        resolve("true") // reject("false")
    },1000)
})

myPromise.then(data=>{
    console.log(data)
},err=>{
    console.log(err)
})

先不考虑那么多,也不像很多博客里写的那种,一上来就给非常完美的思路和非常标准的 Promise/A+ 规范.

我就按照自己思路一点点的来做.


step 01 从代码代用级别摸清内部构造

  • 一个 Promise 对象,有三个状态.
  • 初始状态是 pending.
  • 成功状态是 resolve.
  • 失败状态是 reject.
image.png

然后看上述写的代码.

let myPromise = new Promise((resolve,reject) => {
    setTimeout(()=>{
        resolve("true") // reject("false")
    },1000)
})

  • new Promise () 是一个构造函数,没什么好说的.
function MyPromise () {

}

// 肯定是 
let my_promise = new MyPromise()

  • 这个构造函数接受的参数也是一个函数,也没什么好说的.
function MyPromise (func) {
    typeof func === 'function' ===> true
}
  • 在这个函数参数里我们声明了 resolve 和 reject 两个形参.
function MyPromise((resolve,reject) => {
    typeof reject === 'function'  // true
    typeof resolve === 'function' // true
})
  • 在后续的使用参数的过程中,我们可以知道,是构造函数传入的执行器,在内部调用了这两个reject 和 resolve
function MyPromise() {
    //xxx    
}

new MyPromise((resolve,reject) => {
    setTimeout(() => {
        reslove(data) // or reject (err)
    },1000)
})

  • reject 和 resolve 肯定不是由传递进去的参数自己提供的.
function MyPromise() {
    //xxx    
}

new MyPromise((resolve,reject) => {
    setTimeout(() => {
    // resovle 哪来的?
        reslove(data) // or reject (err) // reject 哪来的?
    },1000)
})

  • 那么就只有一个可能,那是 Promise 内部提供的两个功能函数.
function MyPromise () {
    function resolve () {}
    function reject () {}
}
  • 其中 resolve 函数别的不管,起码应该会把当前 promise 对象的状态由 pending --> resolve reject 同理, 从 pending --> reject
function MyPromise () {
    function resolve () {
        // pending ---> resolve
    }
    function reject () {
        // pending ---> reject
    }
}

  • 调用 then 实例方法,会将两种状态的回调函数传递进去.
new MyPromise(executor).then(resolveData=>{},rejectData=>{})

step 02 根据上述推理,猜测内部写法.

1. 首先是一个构造函数

function MyPromise () {

}

2. 其次需要接受一个执行器.

function MyPromise (executor) {
    
}

3. 实例化出来的 Promise 对象有三种状态

MyPromise.prototype.states = {
    PENDING: 'PENDING',
    RESOLVE: 'RESOLVE',
    REJECT: 'REJECT'
}

4. 一开始 promise 对象是 pending 状态

function MyPromise (executor) {
    this.state = this.states.PENDING // pending.
}

5. 内部起码得有两个 reject 和 resolve.要不然executor里面的reject 和 resolve 哪来的? 或者把这两个方法

function MyPromise (executor) {
    this.state = this.states.PENDING // pending.
    const that = this // 挂载 this
    function resolve () {
        if (that.state === this.states.PENDING) {
            that.state = this.states.PENDING
        }
    }
    
    function reject () {
        if (that.state === this.states.PENDING) {
            that.state = this.states.REJECT
        }
    }
}

或者觉得挂载 this 麻烦,我就把 resolve 和 reject 挂在 prototype 上.

MyPromise.prototype.reject = function () {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT
    }
}

MyPromise.prototype.resolve = function () {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE
    }
}

到目前为止,我们可以通过 executor 内部调用 reject 或者 resolve 修改某个 Promise 的状态了.

在回到我们使用 ES6 的 Promise 代码中.

new Promise ((resolve,reject) => {
    resolve(someData) // or reject(someError)
}) 

可以知道 reslove 和 reject 是可以接受参数的.

  • 对于 resolve 来说,参数就是 executor 执行成功返回的的值.
  • 对于 reject 来说,参数就是 executor 执行失败返回的值.

6. 给 reject 和 resolve 来设置一个形参

// reject 给 error 形参
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT
    }
}

// resolve 给 data 形参
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE
    }
}

7.我们通过 then 传递了两个函数,第一个是成功的回调. 第二个是失败的回调.

MyPromise.prototype.then = function (onResolve,onReject) {
    this.onResloveCallback = onResolve
    this.onRejectCallback = onReject
}


在回到 ES6 的 Promise 调用代码中.

let p = new Promise((resolve,reject) => {
    setTimeout(()=>{
        resolve(data形参) // reject(失败形参)
    },1000)
})

p.then(data=>{
    data === data 形参
})

8.resolve的形参传递给了then的第一个回调函数的参数,error传递给了then的第二个回调函数的参数

// reject 给 error 形参
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT
        this.error = error
    }
}

// resolve 给 data 形参
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE
        this.data = data
    }
}


9. 状态改变完成之后,就需要调用从then函数接受过来的两个函数参数了(reject&resolve)

// reject 给 error 形参
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT // 该状态
        this.error = error // 记录值
        this.onRejectCallback(this.error) // 调用回调
    }
}

// resolve 给 data 形参
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE // 改状态
        this.data = data // 记录值
        this.onResolveCallback(data) // 调用回调
    }
}

完整的代码

function MyPromise (executor) {
    this.state = this.states.PENDING
    this.value = null // 用于记录executor内部的reject or resolve 传递回来的值.
    this.onRejectCallback = null
    this.onResolveCallback = null
    
    
    executor(this.resolve.bind(this),this.reject.bind(this))

}

MyPromise.prototype.states = {
    PENDING: 'PENDING',
    RESOLVE: 'RESOLVE',
    REJECT: 'REJECT'
}

// reject 给 error 形参
MyPromise.prototype.reject = function (error) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.REJECT // 该状态
        this.error = error // 记录值
        this.onRejectCallback(this.error) // 调用回调
    }
}

// resolve 给 data 形参
MyPromise.prototype.resolve = function (data) {
    if (this.state === this.states.PENDING) {
        this.state = this.states.RESOLVE // 改状态
        this.data = data // 记录值
        this.onResolveCallback(data) // 调用回调
    }
}



step 03 . 测试MyPromise 的 resolve 和 reject

测试调用自定义的promise ---> pending --> resolve

new MyPromise((resolve, reject) => { 
  setTimeout(() => {
    resolve(1)
  }, 1000);
}).then(data => { console.log(data) }, error => { console.log(error) })

正确输出.

image.png

测试调用自定义的promise ---> pending --> reject

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject('error')
  }, 1000);
}).then(data => { console.log(data) }, error => { console.log(error) })

错误输出.

image.png

第一阶段手写 Promise 达成目标:

  • 状态改变 [✅]
  • 正确的调用then的函数接受的两个回调函数.[✅]

补充一点:

如果把 reject 和 resolve 写在 prototype 上,然后直接通过 this.reject/resolve 传递会导致 this 指向的错误(obj.method当函数传递,只能传 method, 不会传 obj,真正的纯函数传递).所以,上述代码一定要写成.

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

推荐阅读更多精彩内容

  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,698评论 1 56
  • 一、Promise的含义 Promise在JavaScript语言中早有实现,ES6将其写进了语言标准,统一了用法...
    Alex灌汤猫阅读 818评论 0 2
  • 你不知道JS:异步 第三章:Promises 在第二章,我们指出了采用回调来表达异步和管理并发时的两种主要不足:缺...
    purple_force阅读 2,052评论 0 4
  • 目录:Promise 的含义基本用法Promise.prototype.then()Promise.prototy...
    BluesCurry阅读 1,488评论 0 8
  • Promiese 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,语法上说,Pr...
    雨飞飞雨阅读 3,348评论 0 19