Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

git-lt/MPromise

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

5 Commits

Repository files navigation

依据标准自定的Promise工具类,便于学习理解Promise,也可尝试用于项目使用,百分百通过了 Promise/A+ 的标准测试。

如果你喜欢,烦请给个 star ,谢谢!

详细实现过程,请阅读我的分享 深入理解 Promise (中)

扩展方法

静态方法

  • all
  • race
  • stop
  • defer
  • resolve
  • reject
  • sequence

实例方法

  • wait
  • always
  • done
  • timeout

扩展方法说明

all

用于并行执行promise组成的数组(数组中可以不是Promise对象,在调用过程中会使用 Promise.resolve(value) 转换成Promise对象),如果全部成功则获得成功的结果组成的数组对象,如果失败,则获得失败的信息,返回一个新的Promise对象

Promise.all = function(iterable){
 var _this = this;
 return new this(function(resolve, reject){
 if(!iterable || !Array.isArray(iterable)) return reject( new TypeError('must be an array') );
 var len = iterable.length;
 if(!len) return resolve([]);
 var res = Array(len), called=false;
 iterable.forEach(function(v, i){
 (function(i){
 _this.resolve(v).then(function(value){
 res[i]=value;
 if(++counter===len && !called){
 called = true;
 return resolve(res)
 }
 }, function(err){
 if(!called){
 called = true;
 return reject(err);
 }
 })
 })(i)
 })
 })
}

使用方式

function fn1(){
 return new Promise(resolve => setTimeout(()=>resolve(1), 3000))
}
function fn2(){
 return new Promise(resolve => setTimeout(()=>resolve(2), 2000))
}
Promise.all([fn1(), fn2()]).then(res=>console.log(res), err=>console.log(err))
// [1, 2]

race

用于并行执行promise组成的数组(数组中可以不是Promise对象,在调用过程中会使用 Promise.resolve(value) 转换成Promise对象),如果某个promise的状态率先改变,就获得改变的结果,返回一个新的Promise对象

Promise.race = function(iterable){
 var _this = this;
 return new this(function(resolve, reject){
 if(!iterable || !Array.isArray(iterable)) return reject( new TypeError('must be an array') );
 var len = iterable.length;
 if(!len) return resolve([]);
 var called = false;
 iterable.forEach(function(v, i){
 _this.resolve(v).then(function(res){
 if(!called){
 called = true;
 return resolve(res);
 }
 }, function(err){
 if(!called){
 called = true;
 return reject(err);
 }
 })
 })
 })
}

使用方式

function fn1(){
 return new Promise(resolve => setTimeout(()=>resolve(1), 3000))
}
function fn2(){
 return new Promise(resolve => setTimeout(()=>resolve(2), 2000))
}
Promise.race([fn1(), fn2()]).then(res=>console.log(res), err=>console.log(err))
// 2

resolve

用于包装任意对象为promise对象,返回一个新的promise,并且状态是resolved

Promise.resolve = function(value){
 if(value instanceof this) return value;
 return executeCallback.bind(new this())('resolve', value);
}

reject

用于包装任意对象为promise对象,返回一个新的promise,并且状态是rejected

Promise.reject = function(value){
 if(value instanceof this) return value;
 return executeCallback.bind(new this())('reject', value);
}

wait

用于一个promise任务结束后等待指定的时间再去执行一些操作

Promise.prototype.wait = function(ms){
 var P = this.constructor;
 return this.then(function(v){
 return new P(function(resolve, reject){
 setTimeout(function(){ resolve(v); }, ~~ms)
 })
 }, function(r){
 return new P(function(resolve, reject){
 setTimeout(function(){ reject(r); }, ~~ms)
 })
 })
}

使用

fn1().wait(2000).then(res=>console.log(res),err=>console.log(err))

这里考虑到,wait 是用于promise实例对象上的,那么为了可以保证链式调用,必须返回一个 新的promise,并且上一步的成功和失败的消息不能丢失,继续向后传递,这里只做延迟处理。

stop

用于中断promise链

通常在 promise链 中去reject或throw,或者是异常报错信息,promise内部都会使用 try...catch 转换为 reject 方法往后传递,无法中断后面的 then 或其它方法的执行,那么这里利用,then 方法中对状态的要求必须不是 Pending 状态的处理才会立即执行回调,在 promise链 中返回一个初始状态的 Promise对象,便可以中断后面回调的执行。

Promise.stop = function(){
 return new this();
}

使用

Promise
 .resolve(1)
 .then(res=>{
	console.log('发生错误,停止后面的执行')
	return Promise.stop();
 })
 .then(res=>console.log(res))
 .catch(err=>console.log(err))

always

无论成功还是失败最终都会调用 always 中注册的回调

Promise.prototype.always = function(fn){
 return this.then(function(v){
 return fn(v), v;
 }, function(r){
 throw fn(r), r;
 })
}

使用

ajaxLoadData()
 .then(res=>console.log(res), err=>console.log(err))
 .always(()=>console.log('关闭loading动画'))

done

由于promise在执行 resolveonResolved 回调时,使用了try...catch,并将错误信息,使用 reject方法 传递了出去,但是如果后面没有注册处理reject的回调函数,那么错误信息将无法得到处理,进而消失不见,难以查觉,所以有了 done 方法。

done方法并不返回promise对象,也就是done之后不能使用 thencatch了,其主要作用就是用于将 promise链 中未捕获的异常信息抛至外层,并不会对错误信息进行处理。

done方法必须应用于promise链的最后

Promise.prototype.done = function(onResolved, onRejected){
 this.then(onResolved, onRejected).catch(function (error) {
 setTimeout(function () {
 throw error;
 }, 0);
 });
}

使用

ajaxLoadData()
 .then(res=>{
 return new Promise((resolve,reject)=>reject('未捕获的错误'))
 }, err=>console.log(err))
 .always(()=>console.log('关闭loading动画'))
 .done()//这里会将错误信息 '未捕获的错误' 抛至外层

defer

Deferred 的简称,叫延迟对象,其实是 new Promise() 的语法糖

与Promise的关系

  • Deferred 拥有 Promise
  • Deferred 具备对 Promise的状态进行操作的特权方法
  • Promise 代表了一个对象,这个对象的状态会在未来改变
  • Deferred对象 表示了一个处理没有结束,在状态发生改变时,再使用Promise来处理结果

优缺点

  • 不用使用大括号将逻辑包起来,少了一层嵌套
  • 但是缺少了Promise的错误处理逻辑
Promise.deferred = Promise.defer = function(){
 var dfd = {}
 dfd.promise = new this(function(resolve, reject) {
 dfd.resolve = resolve;
 dfd.reject = reject;
 })
 return dfd
}

使用

function getURL(URL) {
 var deferred = Promise.deferred;
 var req = new XMLHttpRequest();
 req.open('GET', URL, true);
 req.onload = function () {
 if (req.status === 200) {
 deferred.resolve(req.responseText);
 } else {
 deferred.reject(new Error(req.statusText));
 }
 };
 req.onerror = function () {
 deferred.reject(new Error(req.statusText));
 };
 req.send();
 return deferred.promise;
}

timeout

用于判断某些promise任务是否超时 如一个异步请求,如果超时,取消息请求,提示消息或重新请求

Promise.timeout = function(promise, ms){
 return this.race([promise, this.reject().wait(ms)]);
}

用法

function fn4(){
 return new Promise(resolve=> setTimeout(()=>resolve(1), 3000))
}
Promise
 .timeout(fn4(), 2000)
 .then(res=>console.log(res), err=>console.log('超时'))
// 这里 fn4需要3s执行完成,这里只准在2s内完成,fn4的执行时间就超时了,会输出 `超时`

sequence

用于按顺序执行一系列的promise,接收的函数数组,并不是Promise对象数组,其中函数执行时就返回Promise对象,用于有互相依赖的promise任务

Promise.sequence = function(tasks){
 return tasks.reduce(function (prev, next) {
 return prev.then(next).then(function(res){ return res });
 }, this.resolve());
}

使用

function fn1(){ return new Promise(r=>r(1))}
function fn2(data){ return new Promise(r=>r(1+data))}
function fn3(data){ return new Promise(r=>r(1+data))}
Promise.sequence([fn1,fn2,fn3]).then(res=>console.log(res))
//3

测试

对于自定义的Promise类库,是否符合 Promise/A+ 的标准呢?

社区有一个开源的测试脚本 只需两步,就能检验我们的实现是否符合标准了

//全局安装
npm i -g promises-aplus-tests
//运行测试
promises-aplus-tests Promise.js

下面是我们自定义的Promise类库的测试结果,全部通过

About

自定义的Promise类库,通过Promise/A+测试,包含更多的扩展方法,用于理解学习Promise,也可尝试用于项目

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

Contributors

AltStyle によって変換されたページ (->オリジナル) /