学习过JavaScript的人都知道,JavaScript是单线程作业,这样会有一个很大的缺陷,所有的Ajax,浏览器事件等,都是通过异步去完成。所谓的同步和异步最大的区别无非就是在于同步会阻塞后续代码的执行,然而异步则不会阻塞后续代码的执行。

像setTimeout,setInterval,Ajax这些都是通过异步的回调去做的。拿setTimeout比方来说,及时是把时间设置成0,但是它依然是属于异步方法,任然不会阻塞后续代码的执行。

说到头来无论是setTimeout,Ajax或是其他的异步操作都避免不了的就是回调函数。setTimeout中的function其实就是一个回调函数。

setTimeout(function(){
console.log("我是异步的")
},0)

看一个简单的小例子:

console.log(1)
console.log(2)
console.log(3)
setTimeout(function(){
console.log(4)
},0)
setTimeout(function(){
console.log(5)
},0)
console.log(6)
console.log(7)
console.log(8)

结果:
1,2,3,6,7,8,4,5

如果先看最上面的三行代码,这三行代码是从上之下依次执行的。中间没有任何的异步操作,所以就会依次的打印出1,2,3。接下来就看下整体的代码,在代码中间掺杂了两个异步方法,由于异步操作不会阻塞代码所以就输出了1,2,3,6,7,8,4,5这样的结果。

在做项目的过程中难免会出现一些情况。通过Ajax向后台去请求参数,在返回的参数里面又需要在另一个异步操作里面去使用,依此类推这样的需求如果在同一个页面里面出现N次也就出现了让我们最头疼的一个问题,这也就是我们常说的地狱式回调。

举个例子:

function fn(){
$.ajax({
url:'',
success:funtion(data){
$.ajax({
url:"",
data:data.n,
success:function(data1){
$.ajax({
url:"",
data:data1.n,
success:function(){
......
}
})
}
})
}
})
}

在上面的fn函数中使用了Ajax进行数据请求,服务器返回的参数中,需要用到返回参数里面的n值,才能得到需要的想要的参数,得到n值之后,使用n值作为参数,再次进行ajax请求,依此类推,这里还只是列举了三层,很有可能在实际的项目中会有更多的这样的需求,这样的代码无论是对于后期的维护还是代码的美观性,都是一件很让人头疼的事情,无非就是一场灾难。对于这种情况,还是有解决方案的。

代码:

function fn1(fn){
$.ajax({
url:'',
success:funtion(data){
fn() && fn(data.n)
}
})
}
function fn2(n,fn){
$.ajax({
url:'',
data:{
n:n
},
success:funtion(data){
fn() && fn(data.n)
}
})
}
function fn3(n,fn){
$.ajax({
url:'',
data:{
n:n
},
success:funtion(data){
fn() && fn(data.n)
}
})
}
fn1(function(n1){
fn2(n1,function(n2){
fn3(n2)
})
})

同样的需求,通过回调函数的形式去解决就会方便很多,无论是代码的清晰度还是维护来说都会比较方便,即使这样是解决了一些问题,但仍然没有解决根本性的问题,还是需要通过回调函数去找到各个函数之间一一对应的关系,多多少少的也会带来一些小的困扰。

为了解决类似这样的问题,EcmaScript6的发布推出了Promise对象,Promise的推出无非是所有学习JavaScript人的一个福音,Promise主要就是为了结果异步操作和地狱式回调函数的问题。

从头到尾扯了了这么多,那么到底Promise到底是什么东西?主要能做什么?有什么特点?该怎么使用Promise对象?脑海中出现了一串的问号。

Promise是什么?

Promise简单的来说就是一个容器,里面保存着未来会结束的某个事件。一般来说会是一个异步操作。Promise可以获取到异步操作的消息。

Promise的特点?

  • 对象的状态不会受到外界的影响。Promise代表的是一个异步的操作。一共有三种状态pending(进行中),resolved(已做完)和rejected(已失败),异步操作的结果,决定这Promise最终的状态。成功的则会使用resolved作为回调,如果失败则会使用rejected作为回调,任何的操作都无法改变其中的状态。
  • Promise的状态一旦改变,就永远不会再改变。Promise的改变只有两种情况,Pending –> rejected或者Pending-> resolved,Promise的最大特点也就是在此,如果你错过了他的返回结果,如果再想通过某种方法,去获得Promise的返回结果是获取不到的。

Promise语法应用:

Promise是一个对象,需要使用new得到一个新的Promise对象。

代码示例:

function imgs (url){
return new Promise ((resolve, reject) => {
var oImg = new Image;
oImg.src = url;
oImg.onload = function(){
resolve(this);
}
oImg.onerror = function(){
reject(new Error("图片加载失败"))
}
})
}
var oImg = imgs("http://aaronblog.vip/www/img/banner/banner-1517467815031.jpg");
oImg.then((oimg) => {
console.log(oimg)
}).catch((err) => {
console.log(err)
})

先分析一下上面的代码,在imgs函数中return出去了一个Promise 对象,然而在Promise对象里面,使用new方法新建了一个Image对象,为这个Image对象添加了一个src,并执行性onload,和onerror方法,当图片加载完成后使用resolve方法,并把this(this指向的是Image)作为参数作为传了出去,当图片加载失败的时候使用reject函数,并new一个Error返回出去。在执行imgs函数之后通过oImg变量接收,此时oImg对象得到的就是一个Promise对象。

在Promise的原型上分别挂载着两个方法,then和catch,then代表成功,catch代表的是失败。所以在oImg.then的时候可以拿到传出来的Image对象。如果此时图片加载失败则会走catch,接收到的就是new Error的信息。

需要注意的是,在then函数中一共两个回调函数,第一个是成功,第二个是失败,同样可以捕获到错误

代码示例:

var oImg = imgs("http://aaronblog.vip/www/img/banner/banner-1517467815031.jpg");
oImg.then((oimg) => {
console.log(oimg)
},(err) => {
console.log(err)
})

但是我们一般情况下不会这样去做,都是使用catch去捕获错误。这一点很重要。如果你非要使用这种方法也是可以的。不会出现问题。

实例应用:

function $ajax(data){
return new Promise(resolve, reject){
$.ajax({
url:"",
data:data,
success:resolve,
error:reject
})
}
}
var P1 = $ajax({a:1});
P1.then((data) => {
console.log("我成功了!")
}).catch((err) => {
console.log("我失败了!")
})

`

上面ajax方法就是使用Promise对象进行了二次封装,当success的时候去使用resolve,error的时候则去执行reject方法,无论是失败还是成功,resolve,reject都可以接收到对应函数返回的结果。这样的话通过P1.then的方法就可以轻松的完成回调了。

刚才在上面也有提到过Promise对象上挂载着两个方法then和catch,所以then和catch可以连续调用。

代码示例:

var P1 = $ajax({a:1});
P1.then((data) => {
console.log("我成功了!")
}).catch((err) => {
console.log("我失败了!")
}).then(() => {
console.log("我执行了!")
}).then(() => {
console.log("我是最后执行!")
})

通过上面的代码如果Ajax请求成功,会依次打印“我成功了!”,“我执行了!”,“我是最后执行!”,从这里可以分析出,这里的then是一步一步执行的,而不是异步执行的。记住这一点很重要。

Promise实例的异步方法和then中返回的Promise的应用。

第一种情况

let p2 = new Promise ( ... )
let p1 = new Promise ( (resolve, reject) => {
resolve(p2)
} )

在p1中成功之后传入了p2,需要注意的是,p1中的resolve执行与不执行完全取决于p2的返回状态,如果p2返回的是成功,则p1.then会执行,否则是相反的。则p1则会走向catch方法。

第二种情况

let p3 = new Promise ( (resolve, reject) => {
resolve()
} )
let p4 = new Promise ( ... )
p3.then(
() => return p4
)

在p3执行了then方法之后又return出去了p4此时,如果在p3后面再去使用then方法的话则会指向p4的then,而不再指向p3。catch则会同属于p3和p4。这种情况一般应用于,几个方法使用同样的操作去调整错误信息。

以上是promise需要着重掌握的部分,下面在介绍一下关于promise的其他相关的API的使用

上面说过Promise原型上挂载了两个方法then和catch,分别用来接受成功和失败的状态,但是除了这两个方法以外,还有其他的方法,由于这些方法不经常使用就简单的介绍一下。

Promise其他API

Promise.resolve()/Promise.reject()

这两种方法会把一个对象封装成一个Promise对象,两者唯一不同的地方就是Promise.resolve()会根据参数的情况返回不同的Promise。

需要注意的是:

  • 如果参数是Promise对象的话,则会直接返回传入的Promise对象。
  • 参数带有then方法,转换成Promise对象之后立即执行then方法。
  • 参数不带then方法,不是对象或没有参数,返回的则是Promise的失败状态。

代码示例:

var p1 = Promise.resolve([1, 2, 3]);
p1.then(function(value) {
console.log(value);
//[1, 2, 3]
});

Promise.all

这个方法接收的是一个数组,可以接收Promise对象,如果传入的不是Promise对象则,会默认的调用Promise.resolve()将其转换成Promise对象。在all()方法后面可以使用then方法,去接收参数,then方法里面存放的是一个数组,与传入的Promise对象的顺序是一一对应的关系。没有返回值则是undefined。
需要注意的是在使用all方法的时候,如果传入的Promise对象,其中的任何一个失败了,则就会走向catch,将不会再等待其他Promise对象的返回结果。

代码示例:

var p1 = Promise.resolve(3);
var p2 = 42;
var p3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
Promise.all([p1, p2, p3]).then(function(values) {
console.log(values);
});
// expected output: Array [3, 42, "foo"]

上面代码p2不是一个promise对象,默认调用了Promise.resolve()转换成了promise对象,并返回了出去。

Promise.race

“比赛(竞速)”方法,这方法接收的同样也是一个数组,其传入的参数如果不是一个promise对象,则会调用Promise.resolve()方法,将其转换成Promise对象。

这个方法如果传入的数组中的Promise对象,哪个先接收到结果就走入then成功,函数如果全部都失败的,则会走向catch。

代码示例:

var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'two');
});
Promise.race([p1, p2]).then(function(value) {
console.log(value);
//100
});

上面代码使用setTimeout模拟异步操作,由于p2的时间要比p1快很多所以输出的结果就是2了。

Promise.done

这个方法与Promise.then是类似的,同样可以提供resolved和rejected方法,也可以不提供任何的参数,其主要的目的是为了捕获then或catch尾端没有捕获到的错误。

Promise.finally

作为Promise的后续方法,无论是成功或是失败都会走这个方法。

结语

Promise对象可以高效的解决地狱式回调,使得代码变得更加清晰,易于维护。Promise虽然有他的好处,但是对于解决异步的解决方案,Es6/Es7还提出了其他的解决方案,以后有时间再详细的和大家说一下,最后感谢大家花费这么长时间阅读这篇文章。如果有什么异议,可以联系我或者在文章下方留言。

浅谈Promise的更多相关文章

  1. 浅谈Promise原理与应用

    在JavaScript中,所有代码都是单线程.由于该“缺陷”,JavaScript在处理网络操作.事件操作时都是需要进行异步执行的.AJAX就是一个典型的异步操作 对于异步操作,有传统的利用回调函数和 ...

  2. 浅谈Angular的 $q, defer, promise

    浅谈Angular的 $q, defer, promise 时间 2016-01-13 00:28:00  博客园-原创精华区 原文  http://www.cnblogs.com/big-snow/ ...

  3. 浅谈ES6原生Promise

    浅谈ES6原生Promise 转载 作者:samchowgo 链接:https://segmentfault.com/a/1190000006708151 ES6标准出炉之前,一个幽灵,回调的幽灵,游 ...

  4. 浅谈HTML5单页面架构(一)——requirejs + angular + angular-route

    心血来潮,打算结合实际开发的经验,浅谈一下HTML5单页面App或网页的架构. 众所周知,现在移动Webapp越来越多,例如天猫.京东.国美这些都是很好的例子.而在Webapp中,又要数单页面架构体验 ...

  5. AngularJS进阶(二十五)requirejs + angular + angular-route 浅谈HTML5单页面架构

    requirejs + angular + angular-route 浅谈HTML5单页面架构 众所周知,现在移动Webapp越来越多,例如天猫.京东.国美这些都是很好的例子.而在Webapp中,又 ...

  6. 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理

    [微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...

  7. 《浅谈我眼中的express、koa和koa2》好文留存+笔记

    原文 :三英战豪强,思绪走四方.浅谈我眼中的express.koa和koa2 一.回调大坑怎么解决呢? 1.es5可以利用一下第三方库,例如 async 库, 2.或者单纯使用 connect中间件  ...

  8. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

  9. 浅谈 LayoutInflater

    浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...

随机推荐

  1. hdu 2899 Strange fuction 模拟退火

    求  F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100)的最小值 模拟退火,每次根据温度随机下个状态,再根据温度转移 #include& ...

  2. BZOJ_3398_[Usaco2009 Feb]Bullcow 牡牛和牝牛_组合数学

    BZOJ_3398_[Usaco2009 Feb]Bullcow 牡牛和牝牛_组合数学 Description     约翰要带N(1≤N≤100000)只牛去参加集会里的展示活动,这些牛可以是牡牛, ...

  3. BZOJ_5249_Luogu_P4364_[2018多省省队联测]_IIIDX_九省联考2018_JLOI2018_线段树

    BZOJ_5249_[2018多省省队联测]IIIDX_线段树 Description [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐 ...

  4. SA SD SE 区别

    [SA(System Analysis)系统分析师] 通过一系列分析手法把User想要的结果,以各种文件方式表达出来. 此过程着重于工作流程和处理逻辑. 规划系统功能和模块. 定出初步的数据库内容及系 ...

  5. SM干货篇:你应该具备的提问技巧!

    在成为Scrum Master(SM)之前,我曾担任过许多团队的技术负责人.工作内容之一就是做决定,而且我认为自己做得挺好:坚定果断是我性格的一部分. 然而,当我成为Scrum Master之后,这样 ...

  6. PCB设计检查

    一.资料输入阶段1.在流程上接收到的资料是否齐全(包括:原理图.*.brd文件.料单.PCB设计说明以及PCB设计或更改要求.标准化要求说明.工艺设计说明文件)2.确认PCB模板是最新的3. 确认模板 ...

  7. JavaScript使用闭包实现单例模式

    闭包是JS的一种特性,其中一点就是:可以将外部函数的变量保存在内存中,利用这一特性,我们可以用来实现类的单例模式. 首先需要了解何为单例模式: 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问 ...

  8. 死磕 java集合之ArrayBlockingQueue源码分析

    问题 (1)ArrayBlockingQueue的实现方式? (2)ArrayBlockingQueue是否需要扩容? (3)ArrayBlockingQueue有什么缺点? 简介 ArrayBloc ...

  9. 那些年我们一起踩过的Dubbo"坑"

    前言 微服务架构在如今的9102年已经不是什么新鲜的话题了,但是怎么做好微服务架构,却又是一个永恒的话题.比如服务粒度的划分,怎么控制好粗细?服务划分后,对于项目的部署会有什么改变?...  这会是一 ...

  10. 目标检测之YOLO V2 V3

    YOLO V2 YOLO V2是在YOLO的基础上,融合了其他一些网络结构的特性(比如:Faster R-CNN的Anchor,GooLeNet的\(1\times1\)卷积核等),进行的升级.其目的 ...