先上一个大家比较熟悉的例子;
以前我们写jquery ajax请求的时候,都是这样写:
$.ajax({url: "test.json",success: function(){alert("hello world!");},error:function(){alert("error");}});
后来呢在jquery1.7版本后,我们开始这样写:
$.ajax("test.json").done(function(){ alert("hello world!"); }).fail(function(){ alert("error"); });
很明显,更改后的代码更易懂易读了。
这个是jquery的deferred对象,jquery deferred对象就是jQuery对Promises的实现,还其他有很多实现了promises的库供开发者可用。 像微软的 WinJS.Promise, when.js, q, 和dojo.Deferred,它们暴露的都是deferred对象。Promise也已经纳入了ES6,对于jQuery实现Promises我们暂时就不讲了,今天的目的是Promise。
那Promise到底是怎么回事呢?
其实呢,Promise就是一个对象,它是用来实现异步操作的,它可以让异步代码书写起来更优雅,更便于阅读。
Promise的特性:
1、Promise对象有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败),只有异步操作结果可以改变当前状态,其他操作不能改变状态,Promise对象的状态只能从“进行中”改为“已完成”或者从“进行中”改为“失败”。
2、Promise对象的then方法,就是用来指定回调函数。一旦非同步操作完成,就调用指定的回调函数,并总是返回一个promise对象,方便链式调用。而且同一个promise的then可以调用多次。
3、then方法接受两个回调函数作为参数,第一个参数是成功Resolved时的回调,第二个是失败Reject时的回调。第二个参数是可以选的,同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。
我们现在用promise来组织一下代码。
var sleep = function (time) {return new Promise(function (resolve, reject) {console.log('test123');setTimeout(function () {resolve('test');}, time);})
};
(function(){console.log('start');sleep(3000).then(function(data){console.log(data);return sleep(5000);}).then(function(data){console.log(data);console.log('end');})
})();
上面代码中,sleep方法返回一个Promise实例,表示一段时间以后才会发生的结果。首先输出‘start’,然后,sleep执行后,3秒后返回“test”,触发then方法绑定的回调函数,输出回调返回值“test”,又return sleep(5000),这样第一个then的返回值,就是第二个的参数,再次执行sleep,5秒后返回‘test’,输出‘test’、'end';
好像没有什么大不了的,promises的真正强大之处在于多重的链接,在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了:它有个缺点就是链过长以后,满屏幕的then,傻傻分不清业务到底干啥了,
Promise的 api
1、Promise.resolve()
2、Promise.reject()
3、Promise.prototype.then()
4、Promise.prototype.catch()
5、Promise.all() // 所有的都有完成,相当于 且
6、Promise.race() // 完成一个即可,相当于 或
1、Promise.resolve()的作用将现有对象转为Promise对象resolvedl;
Promise.resolve('test')==new Promise(resolve=>resolve('test'))
2、Promise.reject()也是返回一个Promise对象,状态为rejected;
let p=Promise.reject('error') == let p=new Promise((resolve,reject)=>reject('error'));p.catch(data=>{console.log(data);})
生成一个Promise对象的实例对象p,抛出错误,状态就会变为Rejected,调用p.catch()指定的回调函数。
3、.then()方法,then方法是定义在原型对象Promise.prototype上的,作用是为Promise 对象添加回调函数。
let p=new Promise(resolve=>resolve('hello'));
p.then((resolve,reject)=>{console.log(resolve);
}).then(function(){console.log('zhangsan');
})
then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。 可以使用链式操作,指定多个then(),依次执行,上面的代码输出结果:’hello‘,'zhangsan'
4、.catch():发生错误的回调函数,同样,catch方法也是定义在原型对象Promise.prototype上的,事例同Promise.reject()的例子。注:如果Promise的状态已经变为resolved,再抛出错误是无效的。
var p = new Promise(function(resolve, reject) {resolve('success');reject('fail');
});
p.then(function(value) { console.log(value) }).catch(function(error) { console.log(error) });
输出结果:success;
5、Promise.all()的作用是同时执行多个实例,同步并行,且所以的实例都返回resolve状态,Promise才会改变为resolve状态,其中一个为rejected,promise的状态就是rejected;
注:Promise.all 方法的参数可以不是数组,但是必须是一个Iterator接口对象,且返回promise实例,否则报错:error:TypeError: [object Promise] is not iterable!
let p1 =new Promise(function(resolve,reject){resolve(1);
});
let p2 = new Promise(function(resolve,reject){resolve(2);});
let p3 = new Promise(function(resolve,reject){// reject();resolve(3);});
Promise.all([p1, p2, p3]).then(function (results) {console.log('success:'+results);
}).catch(function(r){console.log("error");console.log(r);
});
上面代码输出:success:1,2,3;
把 p3方法改为返回 reject();输出结果是:error;
6、Promise.race()的作用也是同时执行多个实例,只要有一个实例改变状态,Promise就改为那个实例所改变的状态;
let p1 =new Promise(function(resolve,reject){setTimeout(function(){reject(1);},3000)
});
let p2 = new Promise(function(resolve,reject){setTimeout(function(){resolve(2);},1000)
});
Promise.race([p1, p2]).then(function (results) {console.log('success:'+results);
}).catch(function(results){console.log("error:"+results);
});
输出:success: 2;
上面代码p2首先执行完毕,改变状态为resolve,所以promise的状态被改为resolve;
我们把p1的时间改为1000,p2改为3000,也就是说p1先执行完毕,promise的状态也被改变为reject,输出:error:1
如果想深入学习,可以看一下阮一峰的《ES 6标准入门》
的Promise部分http://es6.ruanyifeng.com/#do...,很详细的。