Promise
含义
Promise是异步编程的一种解决方案 .
(1)Promise对象有三种状态:pending,fulfilled,rejected,只有异步操作的结果,可以决定是哪一种状态,其他操作无法改变这个状态
(2)Promise对象状态一旦改变,不会再变,任何时候都可以获取到这个结果,而事件一旦发生,没有监听,就得不到结果了
基本用法
Promise构造函数,可以在回调内用resolve的和reject来改变状态
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
可以用实例的then方法指定resolved状态和rejected状态的回调函数
promise.then(function(value) {
// success
}, function(error) {
// failure
});
Promise新建后就会立即执行
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
异步加载图片
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
Promise实现ajax
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
上面代码中,p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。
注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
p1作为参数传入会将p2的状态覆盖掉,从而执行p1的回调.resolve或者reject以后,还是会继续执行,但是最好写上return直接返回
Promise.prototype.then()
then方法是一个新的Promise实例的方法,then方法的参数时resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
then方法返回的是一个新的Promise实例,可以采用链式编程
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
执行完第一个回调函数,将返回值传入第二个回调函数并执行
如果在then方法内返回一个promise对象,影响接下来的then调用
getJSON("/post/1.json").then(
post => getJSON(post.commentURL)
).then(
comments => console.log("resolved: ", comments),
err => console.log("rejected: ", err)
);
Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。 当触发rejected或then内出现错误时catch捕获
const promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.catch(function(error) {
console.log(error);
});
// Error: test
如果已经变成resolved,在抛出错误就无效了
e 状态已经变成resolved,再抛出错误是无效的。
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test');
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
// ok
Promise.prototype.finally()
finally方法指定不管Promise对象最后状态如何都会执行操作,finally方法总是会返回原来的值
server.listen(port)
.then(function () {
// ...
})
.finally(server.stop);
Promise.all()
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例
const p = Promise.all([p1, p2, p3]);
p的状态由p1、p2、p3决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
如果数组中的promise实例定义了自己跌catch方法,这时候出错,不会触发Promise.all()的catch方法
Promise.race()
Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。一旦其中一个promise中的实例改变,Promise.race()也更着改变
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。
Promise.resolve()
Promise.resolve方法经常起到的作用是将先有对象转成Promise对象
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
(1)参数是一个Promise实例
如果Promise.resolve将不做任何修改、原封不动地返回这个实例。
(2)参数是一个thenable对象
thenable对象指的是具有then方法的对象,比如下面这个对象。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
(3)参数不是具有then方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
上面代码生成一个新的 Promise 对象的实例p。由于字符串Hello不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved,所以回调函数会立即执行。Promise.resolve方法的参数,会同时传给回调函数。
(4)不带有任何参数
直接返回一个promise对象,可以让then放在本轮事件循环结束
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
Promise.reject()
也是返回一个方法返回一个新的Promise实例,实例状态为rejected
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
Promise.try()
可以抛出同步异步的错误,相较于p.catch只能抛出异步错误
但是database.users.get()可能还会抛出同步错误(比如数据库连接错误,具体要看实现方法),这时你就不得不用try…catch去捕获。
try {
database.users.get({id: userId})
.then(...)
.catch(...)
} catch (e) {
// ...
}
上面这样的写法就很笨拙了,这时就可以统一用promise.catch()捕获所有同步和异步的错误。
Promise.try(database.users.get({id: userId}))
.then(...)
.catch(...)