原生js实现Promise
由于浏览器兼容性的限制,我们不得不通过原生js实现Promise方法。
原生的Promise对象包含promise,promiseAll,rase等方法,下面的代码基本上实现了这些方法,但在细微处可能有所区别,主要是为了方便项目使用才这么设计。
promise
promise方法接收一个函数作为参数(如果参数传错会进入异常捕获阶段),参数会被分裂成两个变量一个resolve, reject。改方法返回当前的promise的实例,可以实现链式操作。
具体使用如下
mrChart.promise(function(resolve, reject){ setTimeout(function(){ resolve('3秒后弹出成功!') },3000) setTimeout(function () { reject('4秒后弹出失败!') }, 4000) }).then(function(a) { console.log(a[1]) alert(a[1]) },function(a){ console.log(a[1]) alert(a[1]) })
关于then方法的使用是接收两个参数(都为函数),第一个参数对应于方法resolve,第二个参数对应于reject,如果不传或者传少了,代码会自动给你生成一个空函数,但是这个函数是捕获不到信息的。
promise.all
promise.all方法是为了监听多个promise对象设计的,它接收多个promise作为参数,以实现多个等待的效果
假如我们创建三个promise
var a = mrChart.promise(function (resolve, reject) { setTimeout(function () { resolve('3秒后弹出成功!') }, 3000) }) var b = mrChart.promise(function (resolve, reject) { setTimeout(function () { reject('4秒后失败!') }, 4000) }) var c = mrChart.promise(function (resolve, reject) { setTimeout(function () { resolve('5秒后弹出成功!') }, 5000) })
我们创建一个promise.all方法,监听上面多个promise的对象状态,只有所有的promise都会成功了才会进入到all方法的成功回调,否则会reject(失败)
代码如下:
var d = mrChart.promise.all(a,b,c).then(function(){ console.log('全部都成功了',arguments) }).catch(function(){ console.log('其中有失败的', arguments); })
promise.rase
还有一个方法rase,字面上是奔跑的意思,我理解是单步rase,可以称之为管道的概念,只有其中一个失败或成功(这取决于谁先跑完),类似于看谁先完成就有谁来决定这次promise的状态,正常业务中用到的场景不是很多,这里也只是简单的实现一下,可能具体细节还实现的不好,有兴趣的可以在上面进行扩展。
这里我们看下代码使用的例子
//单步跑 rase var a = mrChart.promise(function (resolve, reject) { setTimeout(function () { resolve('3秒后弹出成功!') }, 3000) }) var b = mrChart.promise(function (resolve, reject) { setTimeout(function () { reject('4秒后失败!') }, 2000) }) var d = mrChart.promise.race(a,b).then(function(){ console.log('谁快执行哪一个--成功的',arguments) },function(){ console.log('谁快执行哪一个--失败的', arguments) }).catch(function(){ console.log('单个异常会成为当前rase的异常'); })
本插件中还用到发布订阅Emiter,通过这个来辅助完成promise的状态
Emiter实现如下:
//事件订阅区域 function Emiter(){ this._events = Object.create(null); } Emiter.prototype.on = function(event, fn){ var vm = this; if (Array.isArray(event)) { for (var i = 0, l = event.length; i < l; i++) { vm.on(event[i], fn); } } else { (vm._events[event] || (vm._events[event] = [])).push(fn); } return vm } Emiter.prototype.once = function (event, fn) { var vm = this; function on() { vm.off(event, on); fn.apply(vm, arguments); } on.fn = fn; vm.on(event, on); return vm } Emiter.prototype.off = function (event, fn) { var vm = this; // all if (!arguments.length) { vm._events = Object.create(null); return vm } // array of events if (Array.isArray(event)) { for (var i$1 = 0, l = event.length; i$1 < l; i$1++) { vm.off(event[i$1], fn); } return vm } // specific event var cbs = vm._events[event]; if (!cbs) { return vm } if (!fn) { vm._events[event] = null; return vm } // specific handler var cb; var i = cbs.length; while (i--) { cb = cbs[i]; if (cb === fn || cb.fn === fn) { cbs.splice(i, 1); break } } return vm } Emiter.prototype.emit = function (event) { var vm = this; var cbs = vm._events[event]; if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs; var args = toArray(arguments, 1); for (var i = 0, l = cbs.length; i < l; i++) { cbs[i].apply(vm,args); } }else{ error('[mrChart error]:Chart:Emiter.emit event is not found'); } return vm }
Emiter源码参考vue2.0的源码实现
下面是完整的代码
/*! * 针对图形化框架设计的promise 插件 * @version 1.0.0 * */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.mrChart = global.mrChart || {}))); }(this, (function (exports) { 'use strict'; function toArray(list, start) { start = start || 0; var i = list.length - start; var ret = new Array(i); while (i--) { ret[i] = list[i + start]; } return ret } function noop(a, b, c) { } var hasConsole = typeof console === 'object' function log() { if (hasConsole) { Function.apply.call(console.log, console, arguments) } } function warn() { if (hasConsole) { var method = console.warn || console.log // http://qiang106.iteye.com/blog/1721425 Function.apply.call(method, console, arguments) } } function error(str, e) { throw (e || Error)(str) } function isObject(input) { // IE8 will treat undefined and null as object if it wasn't for // input != null return input != null && Object.prototype.toString.call(input) === '[object Object]'; } function isFunction(input) { return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; } //事件订阅区域 function Emiter(){ this._events = Object.create(null); } Emiter.prototype.on = function(event, fn){ var vm = this; if (Array.isArray(event)) { for (var i = 0, l = event.length; i < l; i++) { vm.on(event[i], fn); } } else { (vm._events[event] || (vm._events[event] = [])).push(fn); } return vm } Emiter.prototype.once = function (event, fn) { var vm = this; function on() { vm.off(event, on); fn.apply(vm, arguments); } on.fn = fn; vm.on(event, on); return vm } Emiter.prototype.off = function (event, fn) { var vm = this; // all if (!arguments.length) { vm._events = Object.create(null); return vm } // array of events if (Array.isArray(event)) { for (var i$1 = 0, l = event.length; i$1 < l; i$1++) { vm.off(event[i$1], fn); } return vm } // specific event var cbs = vm._events[event]; if (!cbs) { return vm } if (!fn) { vm._events[event] = null; return vm } // specific handler var cb; var i = cbs.length; while (i--) { cb = cbs[i]; if (cb === fn || cb.fn === fn) { cbs.splice(i, 1); break } } return vm } Emiter.prototype.emit = function (event) { var vm = this; var cbs = vm._events[event]; if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs; var args = toArray(arguments, 1); for (var i = 0, l = cbs.length; i < l; i++) { cbs[i].apply(vm,args); } }else{ error('[mrChart error]:Chart:Emiter.emit event is not found'); } return vm } var pN = 'MrPromise'; function isPromise(n){ return n.isPromise || false } function promiseResolve(){ var promise = this; return function(){ try { promise.state = 'resolve'; promise.callbacks[ 0 ]([arguments, 'resolve']); promise.data = arguments; if (promise.emitName !== '') promise.Emit.emit(promise.emitName, [arguments, promise.state]) } catch (e) { promise.state = 'catch'; promise.callbacks[ 2 ]([e, 'catch']) promise.data = e; if (promise.emitName !== '') promise.Emit.emit(promise.emitName, [e, promise.state]) } return promise; } } function promiseReject() { var promise = this; return function () { try { promise.state = 'reject'; promise.callbacks[ 1 ]([arguments, 'reject']) promise.data = arguments; if (promise.emitName !== '') promise.Emit.emit(promise.emitName, [arguments, promise.state]) } catch (e) { promise.state = 'catch'; promise.callbacks[ 2 ]([e, 'catch']) promise.data = e; if (promise.emitName !== '') promise.Emit.emit(promise.emitName, [e, promise.state]) } return promise; } } function promiseResolveAll(){ var promise = this; return function () { promise.state = 'resolve'; promise.callbacks[0]([arguments, 'resolve']); return promise; } } function promiseRejectAll() { var promise = this; return function () { promise.state = 'catch'; promise.callbacks[1]([arguments, 'catch']) return promise; } } function promiseResolveRace() { var promise = this; return function () { promise.state = 'resolve'; promise.callbacks[0]([arguments, 'resolve']); return promise; } } function promiseRejectRace() { var promise = this; return function () { promise.state = 'reject'; promise.callbacks[1]([arguments, 'reject']) return promise; } } function promiseAll(dependent){ // 初始promise状态 this.state = 'pending'; this.isPromise = true; this.dependent = dependent; // resolve catch==reject this.callbacks = [noop, noop]; this.resolve = promiseResolveAll.call(this), this.reject = promiseRejectAll.call(this); this.Emit = new Emiter(); this.emitName = 'ResolveState'; for (var i = 0; i < dependent.length;i++){ dependent[i].bindEmit(this.Emit,'ResolveState'); } var promise = this; var reason = [new Array(dependent.length), new Array(1)]; this.Emit.on(this.emitName,function(){ var n = 0,m = 0; for (var i = 0; i < promise.dependent.length;i++){ var state = promise.dependent[i].state if (state === 'resolve'){ n++; reason[0][i] = promise.dependent[i].data; } else if (state !== 'pending'){ reason[0] = new Array(promise.dependent.length) reason[1][0] = promise.dependent[i].data; } if (state !== 'pending') m++ } if (n === promise.dependent.length){ promise.resolve(reason[0]) reason = [new Array(dependent.length), new Array(1)]; } else if (m === promise.dependent.length){ promise.reject(reason[1]) reason = [new Array(dependent.length), new Array(1)]; } }); return this; } promiseAll.prototype = { constructor: promiseAll, then:function(){ var args = arguments; if (isFunction(args[0])) this.callbacks[0] = args[0] return this; }, catch:function(){ var args = arguments; if (isFunction(args[0])) this.callbacks[1] = args[0] return this; } } function MrPromise(fn) { // 初始promise状态 this.state = 'pending'; this.isPromise = true; // resolve reject catch this.callbacks = [noop,noop,noop]; this.reject = this.resolve = noop; this.Emit = { emit: noop }; this.data = ''; this.emitName = ''; this.resolve = promiseResolve.call(this), this.reject = promiseReject.call(this); fn(this.resolve, this.reject); return this; } function PromiseRace(dependent){ // 初始promise状态 this.state = 'pending'; this.isPromise = true; this.dependent = dependent; // resolve reject catch this.callbacks = [noop, noop, noop]; this.resolve = promiseResolveRace.call(this), this.reject = promiseRejectRace.call(this); this.Emit = new Emiter(); this.emitName = 'ResolveStateRace'; for (var i = 0; i < dependent.length; i++) { dependent[i].bindEmit(this.Emit, 'ResolveStateRace'); } var promise = this; var reason = [new Array(1), new Array(1)]; this.Emit.on(this.emitName, function (data) { if(promise.state !== 'pending') return; if (data[1] === 'resolve'){ promise.resolve(data[0]) } if (data[1] === 'reject') { promise.reject(data[0]) } if (data[1] === 'catch') { promise.state = 'catch'; promise.callbacks[2](data[0]) } }); return this; } PromiseRace.prototype = { constructor: PromiseRace, then: function () { var args = arguments; if (isFunction(args[0])) this.callbacks[0] = args[0] if (isFunction(args[1])) this.callbacks[1] = args[1] return this; }, catch: function () { var args = arguments; if (isFunction(args[0])) this.callbacks[2] = args[0] return this; } } MrPromise.promise = function(){ var args = arguments, l = args.length, fn = args[0]; if (!l) error(pN + '() 请传入一个函数'); if (l > 1) warn(pN + '() 参数长度为1'); if (!isFunction(fn)) error(pN + '() 参数类型不是一个函数'); return new MrPromise(fn) } //单步跑promise MrPromise.promise.race = function(){ var dependent = []; //依赖promise var args = arguments, isAllPromise = false, n = 0, l = args.length; if (!l) error('promiseRace() 参数长度至少1') for (var i = 0; i < args.length; i++) { if (isPromise(args[i])) { n++ dependent.push(args[i]) } } if (n == l) isAllPromise = true if (!isAllPromise) error('promiseRace() 参数必须为promise对象') return new PromiseRace(dependent); } MrPromise.promise.all = function(){ var dependent = []; //依赖promise var args = arguments, isAllPromise = false, n = 0, l = args.length; if (!l) error('promiseAll() 参数长度至少1') for (var i = 0; i < args.length; i++) { if (isPromise(args[i])) { n++ dependent.push(args[i]) } } if (n == l) isAllPromise = true if (!isAllPromise) error('promiseAll() 参数必须为promise对象') return new promiseAll(dependent); } MrPromise.prototype = { constructor: MrPromise, then:function(){ var args = arguments; if (isFunction(args[ 0 ])) this.callbacks[ 0 ] = args[ 0 ] if (isFunction(args[ 1 ])) this.callbacks[ 1 ] = args[ 1 ] return this; }, catch:function(){ var args = arguments; if (isFunction(args[0])) this.callbacks[ 2 ] = args[0] return this; }, bindEmit:function(emit,name){ this.Emit = emit; this.emitName = name; return this; } } exports.promise = MrPromise.promise; Object.defineProperty(exports, '__esModule', { value: true }); })));
下面是完整使用的demo代码
/*测试promise*/ /* mrChart.promise(function(resolve, reject){ setTimeout(function(){ resolve('3秒后弹出成功!') },3000) setTimeout(function () { reject('4秒后弹出失败!') }, 4000) }).then(function(a) { console.log(a[1]) alert(a[1]) },function(a){ console.log(a[1]) alert(a[1]) }) */ /* var a = mrChart.promise(function(resolve, reject){ setTimeout(function () { resolve('3秒后弹出成功!') }, 3000) setTimeout(function() { reject('4秒后弹出成功!') }, 4000); }) .then(function(){ return c(); //这里故意写错 导致异常抛出 },function(){ console.log(11111) }) .catch(function(){ //会进入这里进行捕获 console.log(arguments) }) */ //var c = mrChart.promise('1',2); 异常捕获测试 //var c = mrChart.promise(); 异常捕获测试 /* //promise.all 测试 var a = mrChart.promise(function (resolve, reject) { setTimeout(function () { resolve('3秒后弹出成功!') }, 3000) }) var b = mrChart.promise(function (resolve, reject) { setTimeout(function () { reject('4秒后失败!') }, 4000) }) var c = mrChart.promise(function (resolve, reject) { setTimeout(function () { resolve('5秒后弹出成功!') }, 5000) }) var d = mrChart.promise.all(a,b,c).then(function(){ console.log('全部都成功了',arguments) }).catch(function(){ console.log('其中有失败的', arguments); }) */ /* //单步跑 rase var a = mrChart.promise(function (resolve, reject) { setTimeout(function () { resolve('3秒后弹出成功!') }, 3000) }) var b = mrChart.promise(function (resolve, reject) { setTimeout(function () { reject('4秒后失败!') }, 2000) }) var d = mrChart.promise.race(a,b).then(function(){ console.log('谁快执行哪一个--成功的',arguments) },function(){ console.log('谁快执行哪一个--失败的', arguments) }).catch(function(){ console.log('单个异常会成为当前rase的异常'); }) */
以上只是简单实现了promise的部分功能,更复杂的功能我这里用不到也就没实现,基本上日常使用够了!