Promise已经出现很久了,但鉴于很多初学者还不是很了解也经常有人问到这个,因此就想写一篇文章来讲解一下这方面的知识,本文会尽量用一些简单的实例来说明一些问题,也会尽量做到通俗易懂、简单明了。
参考文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
一、什么是Promise
Promise是ES6原生提供的一个全局的构造函数,它提供了很多方法供我们使用,它解决了异步操作流程中回掉函数层层嵌套的问题。
当然,看完上面的解释,相信不会有几个人能真正看懂(大神除外)。少一份套路,多一份真诚,牛逼还是要吹的,但要把握一个度,接下来我们用一个简单的实例来看下:
//先实例化一个Promise
var pro=new Promise(function(resolve,reject){
var name="李";
resolve(name);//将name传递下去
});
pro.then(function(data){
//输出成功传来的数据
console.log(data);
//先处理数据
var newData1=data+"可";
//将处理后的数据传递下去
return Promise.resolve(newData1);
}).then(function(data){
//输出成功传来的数据
console.log(data);
//先处理数据
var newData2=data+"可";
//将处理后的数据传递下去
return Promise.resolve(newData2);
}).then(function(data){
//输出最终的数据
console.log(data);
//故意抛出一个异常
throw "Promise不想和你说话,并向你抛出了一个异常!!"
}).catch(function(mes){
//这里处理被拒绝或发生异常的情况
//mes参数代表被拒接或异常时携带的信息
console.log(mes);
});
/* 输出结果如下 */
//李
//李可
//李可可
//Promise不想和你说话,并向你抛出了一个异常!!
看着是不是很牛逼也很神奇啊
,哈哈,我猜肯定没有,这写的啥啊,屁用都没有是吧?肯定是的。但是先别急,上面的代码只是让大家先对Promise有个初步的了解,实际应用的时候肯定不会这么简单啊。另外,这里如果还是没搞懂Promise到底是个什么东西也不要着急,毕竟搞对象一直都不是什么容易的事儿,而何况是搞一个这么牛逼的对象是吧。
下面我们就来详细地逐个讲解每个API
二、then和catch方法
then和catch是Promise.prototype上的两个最常用的方法,下面我们将 从一个最简单的实例开始:
var p1 = new Promise(function(resolve, reject) {
resolve("恭喜你,你赢了");//将来传递给下一层的数据就是里面的参数
});
p1.then(function(data) {
var num=Math.random();//生成一个随机数
if(num>0.1){ //用户有90%的可能性会赢
console.log(data);
return;
}
throw "厉害了word哥,这都能输!";
}).catch(function(e) {
console.log(e);//如果输了,则会被嘲讽
});
1.在这里,我们首先实例化了一个Promise,并将一个函数作为参数传入进去,函数中传入了两个参数resolve和reject,需要注意的是这两个参数都是函数,使用方法是resolve(data)和reject(data),参数data可以是任意数据类型,参数data代表了将要传递下去的数据。
2.then用来接收上一层传来的数据并处理数据,必要时可以当前层继续向下传递数据(怎样再继续传递数据接下来会讲)。
3.catch用来捕获异常或捕获拒绝信息,在这里的参数e就代表了抛出的异常信息。
4.需要注意的是只有Promise对象才有向下传递数据的功能,才有then和catch等方法,所以使用这些牛逼的特性之前必须先new Promise().
三、Promise.resolve()和Promise.reject()
resolve和reject是Promise的两个比较常用的静态方法,关于什么是js的静态方法、原型方法和公共方法大家可以去自行百度。
前面我们提到了如何实现数据的多级传递,其实我们一开始举的例子里面就早已经将其实现了,但是看起来貌似有点生涩难懂,有些同学可能已经想到了,根据上面第4点所说的,我们直接在then里面再次new一个Promise并返回不就行了吗,那我们现在就来测试一下,看看是否可行:
new Promise(function(resolve,reject){
resolve(1);
}).then(function(data){
//返回一个Promise的实例
return new Promise(function(resolve,reject){
resolve(data+1);
})
}).then(function(data){
//返回一个Promise的实例
return new Promise(function(resolve,reject){
resolve(data+1);
})
}).then(function(data){
//返回一个Promise的实例
return new Promise(function(resolve,reject){
resolve(data+1);
})
}).then(function(data){
console.log(data);
})
//4
哈哈,恭喜某些同学,还真可以,但是我们刚开始就说了 " 它解决了异步操作流程中回掉函数层层嵌套的问题 ",然而我们现在却已经嵌套了三层,虽然嵌套的不是很多,但是我们追求的是完美不是吗?如果还有更复杂的需求那该怎么办,我们岂不是深陷其中而无法自拔
。
现在我们来看下Promise.resolve()和Promise.reject()是怎么用的:
Promise.reject()比较简单,它返回一个Promise的实例,并且这个被返回的实例携带着被拒绝的原因(传入的参数),一旦遇到Promise.reject整个流程就会中断,catch也会立即接收到这个原因。当然我们也可以使用throw来代替它,效果是一样的。
这里我们主要讲解一下Promise.resolve()。这里偷个懒,直接引用MDN的解释,Promise.resolve(value)方法
返回一个以给定值解析后的Promise实例。但如果这个值是个thenable(即带有then方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态(指resolved/rejected/pending/settled);否则以该值为成功状态返回promise对象。
当然,听了这个我们肯定还是云里雾里的啥都没听懂,没关系,我们用实例说话:
Promise.resolve(1)//1
.then(function(data){
return Promise.resolve(data+1);//2
}).then(function(data){
return Promise.reject(data+1+"时被拒绝");//3
}).then(function(data){
return Promise.resolve(data+1);//4
}).then(function(data){
console.log(data);//结果
}).catch(function(mes){
console.log(mes);//输出拒绝的"原因"
});
//3时被拒绝
我们说了,Promise.resolve会返回一个Promise实例,因此上面的代码也就不难理解了,每次都return一个Promise.resolve,而Promise.resolve又会返回一个Promise实例,因此我们可以接着再后面跟"then",从而实现了多级链式调用。
另外我们也说了,Promise.reject会返回一个Promise的实例,并跳转到catch那一层,catch也会得到被拒绝的原因,因此在3的时候立即中断并被catch捕获。Promise.reject(mes)其实可以用throw mes来代替的,例如这一句代码可以使用throw data+1+"时被拒绝";来代替,
Promise.resolve()的特殊用法1:除了可以传数值、字符串、boolean、数组和普通对象之外还可以传入另一个Promise对象实例,例如下面的用法:
/*****************1******************/
var original = Promise.resolve(true);
var cast = Promise.resolve(original);
cast.then(function(v) {
console.log(v); // true
});
/*****************2******************/
var original = new Promise(function(resolve,reject){
resolve(true);
});
var cast = Promise.resolve(original);
cast.then(function(v) {
console.log(v); // true
});
Promise.resolve()的特殊用法2:传入一个特殊的对象(含有then方法),例如下面的代码
/***********************************1************************************/
// Resolve一个thennable对象
var p1 = Promise.resolve({
then: function(resolve, reject) { resolve("fulfilled!"); }
});
console.log(p1 instanceof Promise) // true, 这是一个Promise对象
p1.then(function(v) {
console.log(v); // 输出"fulfilled!"
}, function(e) {
// 不会被调用
});
/**********************************2************************************/
// Thenable在callback之前抛出异常
// Promise rejects
var thenable = { then: function(resolve) {
throw new TypeError("Throwing");
resolve("Resolving");
}};
var p2 = Promise.resolve(thenable);
p2.then(function(v) {
// 不会被调用
}, function(e) {
console.log(e); // TypeError: Throwing
});
/************************************3*************************************/
// Thenable在callback之后抛出异常
// Promise resolves
var thenable = { then: function(resolve) {
resolve("Resolving");
throw new TypeError("Throwing");
}};
var p3 = Promise.resolve(thenable);
p3.then(function(v) {
console.log(v); // 输出"Resolving"
}, function(e) {
// 不会被调用
});
细心的小伙伴可能就会发现其实传入thenable(含有then方法的对象)和传入一个Promise的实例,效果基本上是一样的,其用法和传入一个new Promise(function(resolve,reject){})也非常类似,刚开始我们只需要习惯一种用法即可。
四,Promise的其它写法
/******************1******************/
new Promise(function(res,rej){
rej("err1");
}).then(function(){
},function(err){
//捕获到错误
console.log(err);
})
/*******************2*****************/
Promise.reject("err1").then(function(){
},function(err){
//捕获到错误
console.log(err);
})
/*******************3*****************/
Promise.resolve(1)
.then(function(data){
throw new TypeError("err1")
}).then(function(){
},function(err){
//捕获到错误
console.log(err);
});
总之,then可以有第二个参数,这个参数是一个函数,用来捕获上一层可能会传来的错误。
五、Promise的其它静态方法
Promise除了上面提到的resolve和reject方法之外,还有两个静态方法
-
Promise.all()
请原谅我比较懒,再次复制MDN的解释,Promise.all(iterable)
方法返回一个promise,该promise会等iterable参数内的所有promise都被resolve后被resolve,或以第一个promise被reject的原因而reject 。
接下来还是先看几个实例吧:
/*****************1*********************/
var promise = Promise.resolve(3);
Promise.all([true, promise])
.then(values => {
console.log(values); // [true, 3]
});
/*****************2*********************/
var arr=[1,Promise.reject("err"),Promise.resolve(3)];
Promise.all(arr)
.then(values =>{
console.log(values);//不会执行
}).catch(err => {
console.log(err);//err
});
/*****************3*********************/
var arr=[1,Promise.reject("err"),Promise.resolve(3)];
Promise.all(arr)
.then(values =>{
console.log(values);//不会执行
},err => {
console.log(err);//err
});
总结一下,Promise.all的参数是一个数组,这个数组里面的元素可以是Promise的实例,也可以不是,如果不是(比如传入基本数据类型),则会被自动转换为一个Promise对象,最终会在then的第一个参数里得到一个数组,这个数组里面的每个值对应着当时传入的数组里每个元素(是一个Promise的实例)resolve的值。如果传入数组里面的某一个元素被reject了,那么后面将会立即捕获到这个reject的原因,并且不再理会其它传入的promise是否被resolve。
-
Promise.race()
Promise.race(iterable)方法返回一个promise,这个promise在iterable中的
任意一个promise被解决或拒绝后,
立刻以相同的解决值
被解决或以相同的拒绝原因被
拒绝。其中iterable是一个存放着若干个Promise的数组。
通俗来讲,
iterable内的每一个Promise的实例就像在同一起跑线上的运动员,他们即将进行一场百米赛跑,谁先到达终点谁就会被接纳,而其他运动员则竟被忽略掉。
看代码:
/***************************1******************************/
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "一");
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "二");
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "二"
// 两个都解决,但p2更快
});
/***************************2******************************/
var p3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "三");
});
var p4 = new Promise(function(resolve, reject) {
setTimeout(reject, 500, "四");
});
Promise.race([p3, p4]).then(function(value) {
console.log(value); // "三"
// p3更快,所以被解决(resolve)了
}, function(reason) {
// 未被执行
});
/***************************3******************************/
var p5 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "五");
});
var p6 = new Promise(function(resolve, reject) {
setTimeout(reject, 100, "六");
});
Promise.race([p5, p6]).then(function(value) {
// 未被执行
}, function(reason) {
console.log(reason); // "六"
// p6更快,所以被拒绝(reject了)
});
六、实际应用
讲了这么多废话,终于到了实际应用了,对于Promise来说,用到ajax当中是最合适不过了,我们可以使用原生的ajax结合Promise来写一个插件,要求可以链式调用
'use strict';
// A-> $http function is implemented in order to follow the standard Adapter pattern
function $http(url){
// A small example of object
var core = {
// Method that performs the ajax request
ajax : function (method, url, args) {
// Creating a promise
var promise = new Promise( function (resolve, reject) {
// Instantiates the XMLHttpRequest
var client = new XMLHttpRequest();
var uri = url;
if (args && (method === 'POST' || method === 'PUT')) {
uri += '?';
var argcount = 0;
for (var key in args) {
if (args.hasOwnProperty(key)) {
if (argcount++) {
uri += '&';
}
uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
}
}
}
client.open(method, uri);
client.send();
client.onload = function () {
if (this.status >= 200 && this.status < 300) {
// Performs the function "resolve" when this.status is equal to 2xx
resolve(this.response);
} else {
// Performs the function "reject" when this.status is different than 2xx
reject(this.statusText);
}
};
client.onerror = function () {
reject(this.statusText);
};
});
// Return the promise
return promise;
}
};
// Adapter pattern
return {
'get' : function(args) {
return core.ajax('GET', url, args);
},
'post' : function(args) {
return core.ajax('POST', url, args);
},
'put' : function(args) {
return core.ajax('PUT', url, args);
},
'delete' : function(args) {
return core.ajax('DELETE', url, args);
}
};
};
// End A
// B-> Here you define its functions and its payload
var mdnAPI = 'https://developer.mozilla.org/en-US/search.json';
var payload = {
'topic' : 'js',
'q' : 'Promise'
};
var callback = {
success : function(data){
console.log(1, 'success', JSON.parse(data));
},
error : function(data){
console.log(2, 'error', JSON.parse(data));
}
};
// End B
// Executes the method call
$http(mdnAPI)
.get(payload)
.then(callback.success)
.catch(callback.error);
// Executes the method call but an alternative way (1) to handle Promise Reject case
$http(mdnAPI)
.get(payload)
.then(callback.success, callback.error);
// Executes the method call but an alternative way (2) to handle Promise Reject case
$http(mdnAPI)
.get(payload)
.then(callback.success)
.then(undefined, callback.error);
$http(mdnAPI)
.get(payload)
.then(function(data){
console.log(data);
//这里可以继续return一个请求的发送
//例如return $http(mdnAPI).get(payload)
//内部会return一个Promise的实例,因此下面就可以继续"then"
})
.catch(callback.error);
七、Promise的兼容性
- 大小: 31.9 KB
- 大小: 37 KB
分享到:
相关推荐
"牛B装备--隐藏文件夹"可能是指一种特殊设计的隐藏文件夹工具或者方法,它允许用户创建并管理难以发现的文件夹,以增强数据的安全性。 在Windows操作系统中,隐藏文件和文件夹的功能是内置的。用户可以通过以下步骤...
这款“逆天超级牛B的jQuery-UI后台管理模板”正是基于这样的理念,将功能强大的jQuery-UI组件与后台管理需求相结合,打造出了一个高效、易用的后台界面。 首先,我们来深入了解一下jQuery-UI的主要组件: 1. **...
标题中的“最有用的牛B东东--System.Diagnostics.Process.Start()”指的是在.NET框架中一个非常实用且强大的功能,即`System.Diagnostics.Process.Start()`方法。这个方法允许开发者启动一个新的进程或者打开一个已...
互联网资讯,技术简介,IT、AI技术,人工智能
互联网资讯,技术简介,IT、AI技术,人工智能互联网资讯,技术简介,IT、AI技术,人工智能互联网资讯,技术简介,IT、AI技术,人工智能互联网资讯,技术简介,IT、AI技术,人工智能互联网资讯,技术简介,IT、AI技术...
这款“超级牛B的Jquery-UI后台管理模板”无疑是为开发者构建高效、美观的后台管理系统提供了极大的便利。 首先,我们要理解jQuery-UI的核心组件。它包括但不限于以下几个方面: 1. **对话框(Dialog)**:jQuery-...
1.本次修改,把所有明文修改成加密方式,大家可以放心加密后免杀效果依然很好! 2.为了确保各位使用达到良好的免杀效果.希望别去特意胡乱使用.[上报方法了对大家都不好] 3.本次总共提供15个免杀方案,希望大家会喜欢.
辞职信是一种正式表达个人离职意愿的方式,通常包含对工作经历的回顾、离职原因的阐述以及对未来的规划和个人情感的表达。在这封护理员和施工员的辞职信中,我们可以提炼出以下几个关键知识点: ...
【标题】:“牛B硬件信息修改大师-一键修改电脑所有信息” 这个软件工具的主要功能是让用户能够方便快捷地更改他们的计算机硬件信息。在IT行业中,硬件信息修改可能涉及到多个方面,包括但不限于CPU型号、内存容量...
4轴加工是在3轴(X、Y、Z)的基础上增加了一个旋转轴,通常为A轴或B轴,使得工件可以进行倾斜切割,扩大了加工范围。5轴加工则更进一层,除了4轴的旋转外,还增加了一个C轴,允许工件或刀具在五个自由度上移动,极大...
### Excel公式大全操作应用实例(史上最全) #### 身份证相关信息提取 1. **从身份证号码中提取出生年月日:** 可以使用 `MID` 函数结合 `LEFT` 和 `RIGHT` 函数来提取出生日期。例如,假设身份证号码存储在A1单元格...
【PHP实例开发源码-牛逼CMS新闻系统源码 php版.zip】是一个包含PHP源代码的压缩包,专为开发者提供了一个实际的CMS(内容管理系统)新闻系统的开发示例。这个源码是基于PHP编程语言实现的,适用于那些希望学习或深入...
最牛逼的Linux awk命令详细介绍和实例快速学习
牛逼细胞增殖讲解.pptx
【PHP实例开发源码-牛逼CMS地方门户网站系统 php版.zip】是一个包含了PHP编程语言实现的地方门户网站系统的完整源代码。这个系统可能是一个功能丰富的平台,旨在为特定地区提供信息、服务和互动交流功能。让我们深入...
易语言E-牛B模块.ec
### 基于WEKA的高校图书馆流通数据的数据挖掘实例 #### 一、引言 随着信息技术的发展,数据挖掘已成为从海量数据中提取有价值信息的关键技术。Weka是一款由新西兰怀卡托大学开发的开源软件,它包含了丰富的数据...
牛B硬件信息修改大师黑狼破解版可以一键更改,能够自动修改电脑硬件的所有信息,包括用户和组、网卡mac地址、产品ID号、系统注册信息、系统版本及安装时间、系统序列号、硬盘序列号、游览器信息以及本地时区等,同时...
同时还带有清除临时文件、cookies、错误文件以及一些无用的bak、old、log等文件,你也可以自定义进行修改部分信息,可以说这是一款牛B硬件信息修改大师,该软件操作简便、而且非常的小,只有区区1M多。 该款牛B硬件...