手把手教你实现一个完整的 Promise es6学习笔记5--pr
用过 Promise,但是总是有点似懂非懂的感觉,也看过很多文章,还是搞不懂 Promise的 实现原理,后面自己边看文章,边调试代码,终于慢慢的有感觉了,下面就按自己的理解来实现一个 Promise。 ? 已将每一步的代码都放在了 github 上,方便大家阅读。如果觉得好的话,欢迎star。 想要完全理解代码,需要理解 this 和闭包的含义。 ? Promise是什么简单来说,Promise 主要就是为了解决异步回调的问题。用 Promise 来处理异步回调使得代码层次清晰,便于理解,且更加容易维护。其主流规范目前主要是 Promises/A+ 。对于 Promise 用法不熟悉的,可以参看我的这篇文章——es6学习笔记5--promise,理解了再来看这篇文章,会对你有很大帮助的。 在开始前,我们先写一个 promise 应用场景来体会下 promise 的作用。目前谷歌和火狐已经支持 es6 的 promise。我们采用 setTimeout 来模拟异步的运行,具体代码如下: function fn1(resolve,reject) { setTimeout(() { console.log('步骤一:执行'); resolve('1'); },500); } fn2(resolve,1)">() { console.log('步骤二:执行'); resolve('2'new Promise(fn1).then((val){ console.log(val); return new Promise(fn2); }).then(return 33; }).then((val){ console.log(val); }); 最终我们写的promise同样可以实现这个功能。 初步构建下面我们来写一个简单的 promsie。Promise 的参数是函数 fn,把内部定义 resolve 方法作为参数传到 fn 中,调用 fn。当异步操作成功后会调用 resolve 方法,然后就会执行 then 中注册的回调函数。 Promise(fn){ //需要一个成功时的回调 var callback; 一个实例的方法,用来注册异步事件 this.then = (done){ callback = done; } resolve(){ callback(); } fn(resolve); } 加入链式支持下面加入链式,成功回调的方法就得变成数组才能存储。同时我们给 resolve 方法添加参数,这样就不会输出 undefined。 Promise(fn) { var promise = this,value = null; promise._resolves = []; (onFulfilled) { promise._resolves.push(onFulfilled); ; }; resolve(value) { promise._resolves.forEach( (callback) { callback(value); }); } fn(resolve); }
但是,目前的 Promise 还存在一些问题,如果我传入的是一个不包含异步操作的函数,resolve就会先于 then 执行,也就是说 promise._resolves 是一个空数组。 为了解决这个问题,我们可以在 resolve 中添加 setTimeout,来将 resolve(value) { setTimeout(() { promise._resolves.forEach( (callback) { callback(value); }); },0); } 引入状态剖析 Promise 之基础篇 说 这里存在一点问题: 如果 Promise 异步操作已经成功,之后调用 then? 注册的回调再也不会执行了,而这是不符合我们预期的。 对于这句话不是很理解,有知道的可以留言说下,最好能给实例说明下。但我个人觉得是,then 中的注册的回调都会在 resolve 运行之前就添加到数组当中,不会存在不执行的情况啊。 接着上面的步伐,引入状态: []; promise._status = 'PENDING'; (onFulfilled) { if (promise._status === 'PENDING') { promise._resolves.push(onFulfilled); ; } onFulfilled(value); return this; }; resolve(value) { setTimeout((){ promise._status = "FULFILLED"; promise._resolves.forEach( (callback) { callback(value); }) },0); } fn(resolve); } 每个 Promise 存在三个互斥状态:pending、fulfilled、rejected。Promise 对象的状态改变,只有两种可能:从 pending 变为?fulfilled 和从 pending 变为 rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。 加上异步结果的传递目前的写法都没有考虑异步返回的结果的传递,我们来加上结果的传递: resolve(value) { setTimeout((){ promise._status = "FULFILLED"; promise._resolves.forEach( (callback) { value = callback(value); }) },1)">); } 串行 Promise串行 Promise 是指在当前 promise 达到 fulfilled 状态后,即开始进行下一个 promise(后邻 promise)。例如我们先用ajax从后台获取用户的的数据,再根据该数据去获取其他数据。 这里我们主要对 then 方法进行改造: (onFulfilled) { new Promise((resolve) { function handle(value) { var ret = isFunction(onFulfilled) && onFulfilled(value) || value; resolve(ret); } if (promise._status === 'PENDING') { promise._resolves.push(handle); } else if(promise._status === FULFILLED){ handle(value); } }) }; then 方法该改变比较多啊,这里我解释下:
有些人在这里可能会有点犯晕,有必要对执行过程分析一下,具体参看以下代码: fn1).then(fn2).then(fn3)}) fn1,fn2,fn3的函数具体可参看最前面的定义。
?但这里还存在一个问题,就是我们的 then 里面函数不能对 Promise 对象进行处理。这里我们需要再次对 then 进行修改,使其能够处理 promise 对象。 (resolve) { handle(value) { var ret = typeof onFulfilled === 'function' && onFulfilled(value) || value; if( ret && typeof ret ['then'] == 'function'){ ret.then(function(value){ resolve(value); }); } else { resolve(ret); } } FULFILLED){ handle(value); } }) }; 在 then 方法里面,我们对 ret 进行了判断,如果是一个 promise 对象,就会调用其 then 方法,形成一个嵌套,直到其不是promise对象为止。同时 在 then 方法中我们添加了调用 resolve 方法,这样链式得以维持。 失败处理异步操作不可能都成功,在异步操作失败时,标记其状态为 rejected,并执行注册的失败回调。 有了之前处理 fulfilled 状态的经验,支持错误处理变得很容易。毫无疑问的是,在注册回调、处理状态变更上都要加入新的逻辑: (onFulfilled(resolve....... } function errback(reason){ reason = isFunction(onRejected) && onRejected(reason) || reason; reject(reason); } ) { promise._resolves.push(handle); promise._rejects.push(errback); } if(promise._status === 'FULFILLED'){ handle(value); } else if(promise._status === 'REJECTED') { errback(promise._reason); } }) }; function reject(value) { setTimeout(function(){ promise._status = "REJECTED"; promise._rejects.forEach(function (callback) { promise._reason = callback( value); }) },0); } 添加Promise.all方法Promise.all 可以接收一个元素为 Promise 对象的数组作为参数,当这个数组里面所有的 Promise 对象都变为 resolve 时,该方法才会返回。 具体代码如下: Promise.all = (promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to all.'); } Promise.all会返回一个 Promise 实例,该实例直到参数中的所有的 promise 都执行成功,才会执行成功回调,一个失败就会执行失败回调。 日常开发中经常会遇到这样的需求,在不同的接口请求数据然后拼合成自己所需的数据,通常这些接口之间没有关联(例如不需要前一个接口的数据作为后一个接口的参数),这个时候??Promise.all 方法就可以派上用场了。 添加Promise.race方法该函数和?Promise.all 相类似,它同样接收一个数组,不同的是只要该数组中的任意一个 Promise 对象的状态发生变化(无论是 resolve 还是 reject)该方法都会返回。我们只需要对 Promise.all 方法稍加修改就可以了。 Promise.race = Array.isArray(promises)) { new TypeError('You must pass an array to race.'); } return Promise( promises.length; resolver(value) { resolve(value); } ) { promises[i].then(resolver,rejecter); } }); } 代码中没有类似一个 resolveAll 的函数,因为我们不需要等待所有的 promise 对象状态都发生变化,只要一个就可以了。 添加其他API以及封装函数到这里,Promise 的主要API都已经完成了,另外我们在添加一些比较常见的方法。也对一些可能出现的错误进行了处理,最后对其进行封装。 完整的代码如下: ((window,undefined){ resolve 和 reject 最终都会调用该函数 var final = (status,value){ if(promise._status !== 'PENDING') ; 所以的执行都是异步调用,保证then是先执行的 setTimeout((){ promise._status = status; st = promise._status === 'FULFILLED' queue = promise[st ? '_resolves' : '_rejects']; while(fn = queue.shift()) { value = fn.call(promise,value) || value; } promise[st ? '_value' : '_reason'] = value; promise['_resolves'] = promise['_rejects'] = undefined; }); } 参数是一个函数,内部提供两个函数作为该函数的参数,分别是resolve 和 reject var Promise = (resolver){ if (!(typeof resolver === 'function' )) new TypeError('You must pass a resolver function as the first argument to the promise constructor'); 如果不是promise实例,就new一个 if(!(this instanceof Promise)) Promise(resolver); ; promise._value; promise._reason; promise._status = 'PENDING'; 存储状态 promise._resolves = []; promise._rejects = []; // var resolve = (value) { 由於apply參數是數組 final.apply(promise,['FULFILLED'].concat([value])); } var reject = (reason){ final.apply(promise,['REJECTED'].concat([reason])); } resolver(resolve,reject); } Promise.prototype.then = (onFulfilled,onRejected){ 每次返回一个promise,保证是可thenable的 handle(value) { 這一步很關鍵,只有這樣才可以將值傳遞給下一個resolve value; 判断是不是promise 对象 if (ret && typeof ret ['then'] == 'function') { ret.then((value) { resolve(value); },(reason) { reject(reason); }); } else { resolve(ret); } } errback(reason){ reason = typeof onRejected === 'function' && onRejected(reason) || reason; reject(reason); } if(promise._status === 'PENDING'){ promise._resolves.push(handle); promise._rejects.push(errback); }if(promise._status === FULFILLED){ 状态改变后的then操作,立刻执行 callback(promise._value); } REJECTED){ errback(promise._reason); } }); } Promise.prototype.catch = (onRejected){ .then(undefined,onRejected) } Promise.prototype.delay = (ms,1)">this.then((ori){ return Promise.delay(ms,value || ori); }) } Promise.delay = (){ resolve(value); console.log('1'); },ms); }) } Promise.resolve = (arg){ = len 这里与race中的函数相比,多了一层嵌套,要传入index resolver(index) { (value) { resolveAll(index,value); }; } value; ){ resolve(result) } } ) { promises[i].then(resolver(i),rejecter); } }); } Promise.race = Promise; })(window); ? 下载完整版代码,点击 github?,如果觉得好的话,欢迎star。 ? 代码写完了,总要写几个实例看看效果啊,具体看下面的测试代码: var getData100 = (){ (){ resolve('100ms'); }); } var getData200 = (){ resolve('200ms'); }); } var getData300 = (){ reject('reject'); }); } getData100().then((data){ console.log(data); 100ms getData200(); }).then( 200ms getData300(); }).then((data){ console.log(data); },1)"> 'reject' }); Promise.all([getData100(),getData200()]).then( [ "100ms","200ms" ] }); Promise.race([getData100(),getData200(),getData300()]).then( 100ms }); Promise.resolve('resolve').then('resolve' }) Promise.reject('reject函数').then((data){ console.log(data); },1)">(data){ console.log(data); 'reject函数' }) ? 参考文章:1、教你一步一步实现一个Promise - 飞魚 2、剖析 Promise 之基础篇 3、Promise简单实现(正常思路版) 4、大白话讲解Promise(一) 5、Javascript 中的神器——Promise (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |