您的位置:首页 > Web前端 > JavaScript

原生js实现Promise

2019-10-14 10:34 1401 查看

由于浏览器兼容性的限制,我们不得不通过原生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
1758
, [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的部分功能,更复杂的功能我这里用不到也就没实现,基本上日常使用够了!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: