浅析Promise原理
# 一、Promise基础用法
# 1.1 基本用法
new Promise(function(resolve, reject) {
//待处理的异步逻辑
//处理结束后,调用resolve或reject方法
})
- 新建一个
promise很简单,只需要new一个promise对象即可。所以promise本质上就是一个函数,它接受一个函数作为参数,并且会返回promise对象,这就给链式调用提供了基础- 其实
Promise函数的使命,就是构建出它的实例,并且负责帮我们管理这些实例。而这些实例有以下三种状态:
pending: 初始状态,位履行或拒绝fulfilled: 意味着操作成功完成rejected: 意味着操作失败
pending状态的Promise对象可能以fulfilled状态返回了一个值,也可能被某种理由(异常信息)拒绝(reject)了。当其中任一种情况出现时,Promise对象的then方法绑定的处理方法(handlers)就会被调用,then方法分别指定了resolve方法和reject方法的回调函数

var promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// 如果调用了resolve方法,执行此函数
}, function(value) {
// 如果调用了reject方法,执行此函数
});
上述代码很清晰的展示了
promise对象运行的机制。下面再看一个示例:
var getJSON = function(url) {
var promise = new Promise(function(resolve, reject){
var client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
function handler() {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
上面代码中,
resolve方法和reject方法调用时,都带有参数。它们的参数会被传递给回调函数。reject方法的参数通常是Error对象的实例,而resolve方法的参数除了正常的值以外,还可能是另一个Promise实例,比如像下面这样。
var p1 = new Promise(function(resolve, reject){
// ... some code
});
var p2 = new Promise(function(resolve, reject){
// ... some code
resolve(p1);
})
上面代码中,
p1和p2都是Promise的实例,但是p2的resolve方法将p1作为参数,这时p1的状态就会传递给p2。如果调用的时候,p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是fulfilled或者rejected,那么p2的回调函数将会立刻执行
# 1.2 promise捕获错误
Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的别名,用于指定发生错误时的回调函数
getJSON("/visa.json").then(function(result) {
// some code
}).catch(function(error) {
// 处理前一个回调函数运行时发生的错误
console.log('出错啦!', error);
});
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获
getJSON("/visa.json").then(function(json) {
return json.name;
}).then(function(name) {
// proceed
}).catch(function(error) {
//处理前面任一个then函数抛出的错误
});
# 1.3 常用的promise方法
Promise.all方法
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例
var p = Promise.all([p1,p2,p3]);
- 上面代码中,
Promise.all方法接受一个数组作为参数,p1、p2、p3都是Promise对象的实例。(Promise.all方法的参数不一定是数组,但是必须具有iterator接口,且返回的每个成员都是Promise实例。)
p的状态由p1、p2、p3决定,分成两种情况
- 只有
p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数 - 只要
p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
// 生成一个Promise对象的数组
var promises = [2, 3, 5, 7, 11, 13].map(function(id){
return getJSON("/get/addr" + id + ".json");
});
Promise.all(promises).then(function(posts) {
// ...
}).catch(function(reason){
// ...
});
Promise.race方法
Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.race([p1,p2,p3]);
上面代码中,只要
p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值
- 如果
Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理
Promise.resolve
有时需要将现有对象转为
Promise对象,Promise.resolve方法就起到这个作用
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将
jQuery生成deferred对象,转为一个新的ES6的Promise对象
- 如果
Promise.resolve方法的参数,不是具有then方法的对象(又称thenable对象),则返回一个新的Promise对象,且它的状态为fulfilled。
var p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
- 上面代码生成一个新的
Promise对象的实例p,它的状态为fulfilled,所以回调函数会立即执行,Promise.resolve方法的参数就是回调函数的参数 - 如果
Promise.resolve方法的参数是一个Promise对象的实例,则会被原封不动地返回 Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数
var p = Promise.reject('出错啦');
p.then(null, function (error){
console.log(error)
});
// 出错了
# 1.4 Async/await简化写法
function getDataAsync (url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
var res = {
url: url,
data: Math.random()
}
resolve(res)
}, 1000)
})
}
async function getData () {
var res1 = await getDataAsync('/page/1?param=123')
console.log(res1)
var res2 = await getDataAsync(`/page/2?param=${res1.data}`)
console.log(res2)
var res3 = await getDataAsync(`/page/2?param=${res2.data}`)
console.log(res3)
}
async/await是基于Promise的,因为使用async修饰的方法最终返回一个Promise, 实际上,async/await可以看做是使用Generator函数处理异步的语法糖,我们来看看如何使用Generator函数处理异步
# 1.5 Generator
首先异步函数依然是:
function getDataAsync (url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
var res = {
url: url,
data: Math.random()
}
resolve(res)
}, 1000)
})
}
使用
Generator函数可以这样写
function * getData () {
var res1 = yield getDataAsync('/page/1?param=123')
console.log(res1)
var res2 = yield getDataAsync(`/page/2?param=${res1.data}`)
console.log(res2)
var res3 = yield getDataAsync(`/page/2?param=${res2.data}`)
console.log(res3))
}
构造函数
原型方法