Promise对象
Promise是异步编程的一种解决方案,就像一个容器,里面装着未来才会结束的事件的结果,一般是一个异步操作。
Promise的特点:
(1)Promise
对象有三种状态pending
进行中,fulfilled
已成功,rejected
已失败。异步操作的结果决定当前的状态是哪一种,其他操作不会改变这个状态。
(2)Promise
的状态改变只有两种可能,即从pending
变为fulfilled
,从fulfilled
变为rejected
,一旦状态改变就会一直保持这个结果,这时就是resolved
已定型
Promise
一旦新建就会立即执行,无法中途取消,成功或者失败的信息都会体现在回调函数里面,若不设置回调函数,则这些信息不会反应到对象外部
Promise的用法:
根据ES6的规定,Promise
对象是一个构造函数,可以生成Promise
实例,构造函数接受一个回调函数,该回调函数的参数有两个,resolve
和reject
,均由JS引擎提供,不用自己部署;
生成实例后,可以用then方法分别指定resolved
和rejected
状态的回调函数(这里用链式操作的方式)。
var promise = new Promise( function( resolve, reject ){
if(/*异步操作成功*/){
resolve( result );
} else {
reject( error );
}
} ).then( function( result ) {
// success
}, function( error ) {
// failure
});
let promise = new Promise( ( function( resolve, reject ) {
console.log('Promise');
resolve();
} ) ).then( function( result ) {
console.log( 'resolve' );
} );
console.log( 'hello promise' );
以上输出顺序是 Promise, hello promise, resolve
Promise
新建后就会立即执行,因此先输出Promise,then回调函数里面的任务是异步执行的,要等到同步任务执行完才能执行,因此后面依次输出hello promise, resolve。 异步加载图片,成功就调用resolve方法,失败就调用reject
function loadImgAsync( url ) {
return new Promise( function ( resolve, reject ) {
const img = new Image;
img.onload = function () {
resolve( img );
};
img.onerror = function () {
reject( new Error(`Could not load image at ${url}`) );
};
img.src = url;
} );
}
Promise
对象实现的 Ajax 操作,若调用resolve和reject时带参数,则这些参数会被传递给回调函数
const getJSON = function (url) {
const promise = new Promise(function (resolve, reject) {
const hanlder = function () {
// 此处的this就是client
console.log(this);
if (this.reayState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
var client;
if (window.XMLHttpRequest) {
//code for IE7+, Firefox, Chrome, Opera, Safari
client = new XMLHttpRequest();
} else {
//code for IE5, IE6
client = new ActiveXObject("Microsoft.XMLHTTP");
}
client.open('get', url);
client.onreadystatechange = hanlder;
client.responseType = 'json';
client.send(null);
});
return promise;
};
getJSON('/banner.json').then(function (result) {
console.log(result);
}, function (error) {
console.log('阿哦出错辽');
});
resolve的参数可以是一个
Promise
实例
var p1 = new Promise( (resolve, reject) => {
// ....
} );
var p2 = new Promise( (resolve, reject) => {
// ....
resolve(p1);
} );
此时
p1
的状态决定了p2
的状态,若p1
的状态是pending
,则p2
的回调函数就需要等待p1
的状态改变,若p1
的状态是resolve
或reject
,则p2
的回调函数会立即执行。若Promise
状态已经变成resolved
,再抛出错误是无效的
Promise常用的方法
Promise.prototype.catch()
相当于.then(null, rejection)
或.then(undefined, rejection)
指定发生错误时的回调函数,一般在then
方法里面处理成功的回调函数,catch
里面处理失败的回调函数
p.then((val) => console.log('fulfilled:', val))
.catch((err) => console.log('rejected', err));
// 等同于
p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log("rejected:", err));
promise.then(fnuction(result){
// do something
}).catch(function(err){
// throw error
});
Promise.prototype.finally()
不管Promise
对象最后状态如何,finally
里面的代码都会执行
ES2018引入标准
promise.finally(() => {
// 语句
});
// 等同于
promise.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
Promise.prototype.finally()
实现
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
Promise.all()
:将多个 Promise 实例,包装成一个新的 Promise 实例
const p = Promise.all([p1, p2, p3]);
只有p1, p2, p3状态都为
fulfilled
,p的状态才是fulfilled
,但只要有一个rejected
,p的状态就会是rejected
// 只有`booksPromise`, `userPromise`这两个异步操作的结果都返回了,
// 才会触发`pickTopRecommendations`这个回调函数
const databasePromise = connectDatabase();
const booksPromise = databasePromise.then(findAllBooks);
const userPromise = databasePromise.then(getCurrentUser);
Promise.all([
booksPromise,
userPromise
]).then(([books, user]) => pickTopRecommendations(books, user));
如果作为参数的
Promise
实例,自己定义了catch
方法,那么它一旦被rejected
,并不会触发Promise.all()
的catch
方法。
// p2指定自己的catch方法
const p1 = new Promise((resolve, reject) => {
resolve('hello');
}).then(result => result).catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
}).then(result => result).catch(e => e);
Promise.all([p1, p2]).then(result => console.log(result)).catch(e => console.log(e));
// ["hello", Error: 报错了]
// p2没有自己的catch方法
const p1 = new Promise((resolve, reject) => {
resolve('hello');
}).then(result => result);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
}).then(result => result);
Promise.all([p1, p2]).then(result => console.log(result)).catch(e => console.log(e));
// Error: 报错了
Promise.race()
:将多个Promise
实例,包装成一个新的Promise
实例,与Promise.all()
不同的是,只要p1,p2,p3
中的任意一个先改变状态,p
的状态就会跟着改变,先改变的Promise
实例的返回值传递给p
的回调函数
Promise.resolve()
:将现有对象转为 Promise 对象,该对象的状态为resolved
,接收的参数分为四种情况
(1)参数是Promise
实例:不做修改,原样返回
(2)thenable
对象:有then
方法的对象,先转为Promise
对象再立即执行这个then
方法
(3)参数不是具有then
方法的对象,或根本就不是对象:返回一个新的Promise
对象,状态为resolved
(4)无参数:直接返回一个resolved状态的 Promise 对象
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
Promise.reject()
:同Promise.resolve()
,但转之后的Promise
对象状态为rejected
,接收的参数与Promise.resolve()
不同,会直接变为后续方法的参数
const thenable = {
then(resolve, reject) {
reject('出错了');
}
};
Promise.reject(thenable).catch(e => {
console.log(e === thenable)
})
// true
// 接收的不是reject抛出的字符串,而是整个thenable对象
Promise.try()
:模拟try代码块
try {
database.users.get({id: userId})
.then(...)
.catch(...)
} catch (e) {
// ...
}
// 等同于
Promise.try(() => database.users.get({id: userId}))
.then(...)
.catch(...)
Deferred对象
deferred
对象是jQuery用来统一处理回调函数的一个解决方案。主要功能归结为4点
(1)ajax
链式写法
$ajax()
操作完成返回一个deferred
对象,可以进行链式操作
$ajax('xxx')
.done(function(){
// success
})
.fail(function(){
// failure
});
done()
相当于success方法,fail()
相当于error方法
(2)指定同一个操作的多个回调函数
deferred
对象允许自由添加多个回调函数。
// 上面的实例代码完成后还想继续加回调函数,按顺序执行
$ajax('xxx')
.done(function(){
// success
})
.fail(function(){
// failure
});
.done(function(){
// 第二个回调函数
});
(3)
deferred
对象允许为多个事件添加同一个回调函数,使用$when()
$.when($.ajax('xxx'), $ajax('yyy'))
.done(function(){
// success
})
.fail(function(){
// failure
});
若两个
ajax
操作都成功就执行done
回调函数,若有一个失败就执行fail
回调函数
(4)提供回调函数的接口,任何操作都可以使用deferred
对象的各种方法(包括异步同步),指定回调函数,$.when()
的参数只能是deferred
对象
// 假设一个很耗时的操作wait
var wait = function(){
var task = function(){
alert('success');
};
setTimeout(task, 5000);
}
// 用$.when()改写 wait()函数运行完,就会自动运行done()方法指定的回调函数
var dtd = $.Deferred(); // 新建一个deferred对象
var wait = function(){
var task = function(){
alert('success');
dtd.resolve(); // 改变deferred对象的执行状态
};
setTimeout(task, 5000);
return dtd;
}
$.when(wait(dtd))
.done(function(){ alert("success"); })
.fail(function(){ alert("failure"); });
(5)
deferred.resolve()
方法和deferred.reject()
方法
jQuery规定,deferred
对象有三种执行状态----未完成,已完成和已失败。如果执行状态是"已完成"(resolved
),deferred
对象立刻调用done()
方法指定的回调函数;如果执行状态是"已失败",调用fail()
方法指定的回调函数;如果执行状态是"未完成",则继续等待,或者调用progress()
方法指定的回调函数
类似于Promise
的三种状态
(6)deferred.promise()
方法
var dtd = $.Deferred(); // 新建一个deferred对象
var wait = function(){
var task = function(){
alert('success');
dtd.reject(); // 改变deferred对象的执行状态
};
setTimeout(task, 5000);
return dtd;
}
$.when(wait(dtd))
.done(function(){ alert("success"); })
.fail(function(){ alert("failure"); });
dtd.reject();
上面的代码会先弹出success,5秒之后弹出failure,这是因为最下面改变了
dtd
的状态为已完成
,因此会立即触发done
回调函数。
这种可以随意在方法外部改变deferred
对象状态的操作可以用deferred.promise()
防止。即在原来的对象上返回另一个deferred
对象,返回的对象只开放与改变状态无关的方法(done
,fail
),屏蔽与改变执行状态有关的方法(resolve
,reject
),使执行状态不能被改变。
var dtd = $.Deferred(); // 新建一个Deferred对象
var wait = function(dtd){
var tasks = function(){
alert("complete");
dtd.resolve(); // 改变Deferred对象的执行状态
};
setTimeout(tasks,5000);
return dtd.promise(); // 返回promise对象
};
var d = wait(dtd); // 新建一个d对象,改为对这个对象进行操作
$.when(d)
.done(function(){ alert("success"); })
.fail(function(){ alert("failure"); });
d.resolve(); // 此时,这个语句是无效的
// 若要改变执行状态,只能操作原来的deferred对象
// 改进版,将dtd对象变为函数的内部对象
var wait = function(){
var dtd = $.Deferred(); //在函数内部,新建一个Deferred对象
var tasks = function(){
alert("complete");
dtd.resolve(); // 改变Deferred对象的执行状态
};
setTimeout(tasks,5000);
return dtd.promise(); // 返回promise对象
};
$.when(wait())
.done(function(){ alert("success"); })
.fail(function(){ alert("failure"); });
另外一种防止外部改变执行状态的方法,使用
deferred
对象的构造函数。$.Deferred()
可以接受一个函数名作为参数,$.Deferred()
所生成的deferred
对象将作为这个函数的默认参数
$.Deferred(wait)
.done(function(){ alert("success"); })
.fail(function(){ alert("failure"); });
第三种方法:直接在
wait
对象上部署deferred
接口
var dtd = $.Deferred(); // 生成Deferred对象
var wait = function(dtd){
var tasks = function(){
alert("success");
dtd.resolve(); // 改变Deferred对象的执行状态
};
setTimeout(tasks,5000);
};
dtd.promise(wait); // 关键一句
wait.done(function(){ alert("success"); })
.fail(function(){ alert("failure"); });
wait(dtd);
always()
不管状态如何都会执行
参考链接http://es6.ruanyifeng.com/#docs/promise
http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html