从零手写Promise
- 在这个前端疾速倒退的时代,明天不致力,今天变垃圾,尽管这是一个表情包上看到的,置信大家肯定都看过,说切实的,这话是粗,然而事实就是如此。
- 言归正传咱们看看明天的内容把。
- 上次咱们看了js的重点也是难点 “异步”,趁热打铁,明天看看从零手写Promise
咱们先看看代码
首先咱们先来回顾一下Promise的用法
new Promise((resolve,reject)=>{ resolve('正确的')}).then(value =>{ console.log(value);})
- 能够看的到在浏览器打印出了 "正确的"
Promise
的用法并不难,咱们只有晓得new Promise
给Promise
传入function(resolve,reject)
,接着在函数内做须要做的事件即可,等到须要的时候,对这个构造函数.then(res=>{})
即可获取对应的值
上面咱们就来剖析一下Promise应该如何实现
- 首先咱们应该定义一个Promise的构造
- 本文认为es5 的形式
(function (window) { function MyPromise(executor) { // 1 定义MyPromise 构造函数 function resolve(value) { // 定义resolve } function reject(reason) { // 定义reject } MyPromise.prototype.then = function (onResolved,onRejected) { // 定义then } MyPromise.prototype.catch = function (error) { // 定义catch } // 定义执行器的执行 executor(resolve,reject); } window.MyPromise = MyPromise; // 2导出 })(window)
- 接着能够看看上面的执行
(function (window) { // Promise function Mypromise(executor) { const self = this; // 须要定义一下this指向 self.status = 'pennding'; // 初始化状态 self.data = undefined; // promise对象指定一个用于存储后果数据 self.callbacks = []; // 每个元素的构造 { onResolved(){}, onRejected(){}} function resolve(value) { // resolve if (self.status !== 'pennding') { // 因为promise 状态只能批改一次 return; } self.status = 'resolve'; // 扭转为 resolve self.data = value; // 保留value的值 if (self.callbacks.length > 0) { // 如果有待执行的callback函数,立刻执行回调函数onResolved setTimeout(() => { // 以后计划是为了将工作挂在队列中,制作异步 self.callbacks.forEach(callbacksObj => { callbacksObj.onResolved(value) }) },0) } } function reject(reason) { //reject if (self.status !== 'pennding') { // 因为promise 状态只能批改一次 return; } self.status = 'reject'; // 扭转为 reject self.data = reason; // 保留reason的值 if (self.callbacks.length > 0) { // 如果有待执行的callback函数,立刻执行回调函数onRejected setTimeout(() => { self.callbacks.forEach(callbacksObj => { callbacksObj.onRejected(reason) }) },0) } } try { // 如果执行器抛出异样 executor(resolve, reject); } catch (error) { reject(error) } } // Promise.then() Mypromise.prototype.then = function (onResolved, onRejected) { // 假如以后状态还是pennding const self = this; self.callbacks.push({ onResolved, onRejected }) } // Promise.catch() Mypromise.prototype.carch = function (error) { } window.Mypromise = Mypromise;})(window);
<body> <script src="./promise.js"></script> <script> const p = new Mypromise((resolve, reject) => { setTimeout(() => { // 因为拆分 then还未解决,须要p.then 先执行 resolve(1) console.log("我先") }, 100) }) p.then(value => { console.log("onResolve()1", value) }, reason => { console.l("onReject()1", reason) }) p.then(value => { console.log("onResolve()2", value) }, reason => { console.l("onReject()2", reason) }) </script></body>
- 能够看的到执行构造是异步的,第一步胜利了。
下面的代码咱们来解说一下,
- 1、首先是通过es5写的,所以须要导出咱们应用了 闭包
- 2、 创立Promise构造函数 并导出
- 3、咱们应用promise时都晓得往里面传入一个函数,这个函数带有resolve,reject并且在函数外面执行须要执行的操作,
所以这个承受的参数叫做执行器(executor)
- 4、执行器里执行的是两个函数
- 5、咱们都晓得promise有三个状态其中初始化时pendding
- 6、依据
resolve,reject
扭转状态,扭转值
- 7、咱们都晓得promise构造函数还有then办法和catch办法
- 8、别忘了promise也能够throw Error,所以执行器(executor)须要批改一下
- 9、最初调用并执行看看后果
<body> <script src="./promise1.js"></script> <script> const p = new MyPromise((resolve, reject) => { setTimeout(() => { // 因为拆分起因then 还未做对应解决,此时不能扭转状态 resolve(1) console.log("我先") }, 100) }) p.then(value => { console.log("onResolve()1", value) }, reason => { console.l("onReject()1", reason) }) p.then(value => { console.log("onResolve()2", value) }, reason => { console.l("onReject()2", reason) }) </script></body>
- 怎么后果不对呢
- 再来看看 因为间接执行的回调是同步执行的,须要将工作放入队列中
- 此时后果正确了。
- 最初别忘了promise状态只容许批改一次
- 好的下面是简易版的Promise并不包含then的实现。
接下来咱们在下面的根底对then以及整体进行了一个降级
- 代码如下
(function (window) { const PENDDING = 'pendding'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; function MyPromise(executor) { // 定义MyPromises构造函数 const self = this; self.status = PENDDING; self.data = undefined; self.callbacks = []; function resolve(value) { if (self.status === PENDDING) { self.status = FULFILLED; // 扭转MyPromise状态 self.data = value; // MyPromise 的值对应变动 setTimeout(() => { // 异步执行 self.callbacks.forEach(callbacksObj => { // 如果有待执行的callback函数,立刻执行回调 callbacksObj.onResolved(value) }) }) } } function reject(reason) { if (self.status === PENDDING) { self.status = REJECTED; self.data = reason; setTimeout(() => { self.callbacks.forEach(callbacksObj => { callbacksObj.onRejected(reason); }); }) } } try { // MyPromise能够抛出异样 executor(resolve, reject); } catch (error) { reject(error) } } /* MyPromise原型对象的then() 指定胜利和失败回调 返回一个新的回调函数 // 返回的MyPromise后果由onResolved/onRejected的后果决定 */ MyPromise.prototype.then = function (onResolved, onRejected) { // 定义then const self = this; // 指定回调函数的默认值(必须是函数) onResolved = typeof onResolved==='function' ? onResolved : value => value; onRejected = typeof onRejected==='function' ? onRejected : reason => {throw reason}; return new MyPromise((resolve,reject)=>{ // 返回一个新的MyPromise对象 function handle(callback) { // 返回的MyPromise后果由onResolved/onRejected的后果决定 // 1、抛出异样MyPromise后果为失败 reason为后果 // 2、返回的是MyPromise MyPromise为以后的后果 // 3、返回的不是MyPromise value为后果 // 须要通过捕捉获取能力晓得有没有异样 try{ const result = callback(self.data) // 判断是不是MyPromise if ( result instanceof MyPromise){ // 只有then才晓得后果 result.then(value=>resolve(value),reason=>reject(reason)) }else{ resolve(result) } }catch(error){ reject(error) // 返回的后果为reject(error) 下面第一点 } } // 判断以后的status if (self.status === FULFILLED){ // 状态为 fulfilled setTimeout(()=>{ // 立刻执行异步回调 handle(onResolved); }) } else if (self.status === REJECTED){ // 状态为 rejected setTimeout(()=>{ // 立刻执行异步回调 handle(onRejected); }) }else{ // pendding将胜利和失败保留到callbacks里缓存起来 self.callbacks.push({ onResolved(value){ //函数外面调用回调函数 并且依据回调函数的后果扭转MyPromise的后果 handle(onResolved) //为什么这里没有setTimeout,因为下面曾经写到过扭转状态后回去callbacks里循环待执行的回调函数 }, onRejected(reason){ handle(onRejected) } }) } }) } MyPromise.prototype.catch = function (onRejected) { //定义then return this.then(undefined,onRejected) } window.MyPromise = MyPromise; // 导出MyPromise})(window)
- 置信大家看到这个代码跟我是一样的反馈,我去,怎么跟之前不一样了
- 实际上当我入手去实现了手写Promise当前我感觉真的并不难,如果要说难这个then的实现,略微比下面难一些。
- 好了接下来我解说一下
- 1、首先是在原来的根底下来定义了一些常量方便使用
- 2、要晓得Promise.then返回的是什么
- 3、接着判断一下状态
- 4、好的接下来咱们对相应的状态进行操作
- 5、能够看到pendding状态是没有增加定时器的,为什么不是异步呢,因为是往callbacks存储,而后下面执行到对应地位会进行循环判断,且在循环操作时是异步的,,欠缺pendding
- 6、后面的代码能够看得出有四个地位代码类似度很高,所以定义一个函数,进行封装(留神要在new Promise 里)
- 7、再设置一下默认值
- 8、再来欠缺一下catch
接下来咱们进行测试
- 看来没为什么问题
- 再来试试
catch
- 老手第一次写的还是不怎么样,然而每一步都是通过本人是如何学,实实在在的敲进去的,心愿可能帮忙到和我一样想学好promise的敌人。从开始的很难到前面能间接写进去,其实破费的工夫还是不少的,然而收货颇丰啊。