ES6新增Promise用来实现异步函数,如下
new Promise((resolve, reject) => {
setTimeout(function(){
resolve(1000);
}, 1000);
}).then(data => {
console.log(data)
}).catch(err => {
console.dir(err)
});
//1秒之后在控制台打印出1000
通过new关键字创建一个Promise实例,接受一个参数方法,在实例创建时该方法被执行,执行完成之后通过调用resolve()或reject()分别来触发then和catch,通过resolve方法传入参数做为then回调方法的参数,可以实现数据传递。
promise支持通过then方法的参数方法中返回新Promise实例来进行链式调用,并依次执行。
function p(ms){
var f = new Promise((resolve, reject) => {
setTimeout(function(){
console.log(ms)
resolve(ms+1)
}, ms)
})
return f
}
p(1000).then(p).then(p).then(p)
//1秒之后在控制台打印出1000
//1秒之后在控制台打印出1001
//1秒之后在控制台打印出1002
//...
使用Axios发送多个相互依赖的异步请求时可以这样写
//异步请求队列封装
//传入参数对象paras,包含请求地址url,请求参数para,处理返回数据函数func
var axiosQueue = function(config){
//返回新promise实例给下一个then方法
return new Promise(function(resolve, reject){
axios.get(config.url, config.para)
.then(function (res) {
//在这里可以对请求响应数据做一些操作
config.callback(res)
//将操作过的返回数据传给then方法的参数方法作为参数
resolve(res);
}).catch(function (err) {
reject(err)
})
})
}
//链式调用依次执行多个axios方法
axiosQueue({
url:'url1',para:{a:1},callback:function(){}
}).then(axiosQueue({
url:'url2',para:{a:2},callback:function(){}
})).then(axiosQueue({
url:'url3',para:{a:3},callback:function(){
console.log(res.data.data[2]);
}
}))
//链式调用依次执行多个axios方法,并实现参数先后依赖
axiosQueue({
url:'url1',para:{a:1},func:function(){}
}).then(data => {
//将返回的数据作为新请求的参数
var paras = {url:'url2',para:data.data,callback:function(res){}}
//调用axiosQueue,将返回值(即新Promise实例)返回给then,实现链式调用
return axiosQueue(paras)
}).then(data => {
var paras = {url:'url3',para:data.data.data[1],func:function(res){
console.log(res.data.data[2]);
}}
return axiosQueue(paras)
})
//以上代码逻辑还可以这样实现
//通过Promise.resolve方法立即返回一个resolved状态的Promise对象,直接使用then进行链式调用
Promise.resolve().then(() => {
var paras = {url:'url1',para:{a:1},func:function(){}}
return axiosQueue(paras)
}).then(data => {
var paras = {url:'url2',para:data.data.data[0],func:function(){}}
return axiosQueue(paras)
}).then(data => {
var paras = {url:'url3',para:data.data.data[1],func:function(res){
console.log(res.data.data[2]);
}}
return axiosQueue(paras)
})
下面这段转载来的代码可以帮助理解Promise原理
function Promise(fn) {
var state = 'pending',
value = null,
queue = [];
this.then = function (onResolved, onRejected) {
return new Promise(function (resolve, reject) {
handle({
onResolved: onResolved || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
};
function handle(callback) {
if (state === 'pending') {
queue.push(callback);
return;
}
var cb = state === 'resolved' ? callback.onResolved : callback.onRejected,
ret;
if (cb === null) {
cb = state === 'resolved' ? callback.resolve : callback.reject;
cb(value);
return;
}
try {
ret = cb(value);
callback.resolve(ret);
} catch (e) {
callback.reject(e);
}
}
function resolve(newValue) {
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (typeof then === 'function') {
then.call(newValue, resolve, reject);
return;
}
}
state = 'resolved';
value = newValue;
execute();
}
function reject(reason) {
state = 'rejected';
value = reason;
execute();
}
function execute() {
setTimeout(function () {
queue.forEach(function (callback) {
handle(callback);
});
}, 0);
}
fn(resolve, reject);
}
- 调用Promise方法(创建Promise实例),传入fn参数方法,fn接受两个参数,分别是resolve方法和reject方法
- Promise方法设置当前state状态为pending,value为null,调用then方法
- then方法将返回一个新的Promise实例用来实现链式调用,then方法可以接受onResolved和onRejected两个函数分别用来处理resoled和rejected两种状态,在创建新Promise实例的fn传入新的resolve和reject方法,并调用handle方法
- handle方法会将then传入的onResolved,onRejected和新的Promise实例传入的新的resolve、reject四个函数加入queue中
- Promise方法调用fn方法,执行fn方法内的自定义代码,如ajax等
- fn方法执行成功后应使用newValue作为参数来调用resolve方法(此时当前Promise事实上为resolved状态)
- 如果newValue是一个函数对象,即为resolve传入一个新Promise实例,则将resolve和reject作为参数传给这个新Promise实例的then方法并调用,至此当前Promise相当于执行结束,所以不需要更新state和value,直接进入新Promise实例的pending状态 接3
- 如果newValue是一个数据(对象),则将state更新为resolved,value更新为newValue,调用execute方法
- 接6.2 execute方法会遍历queue数组,使用queue中每个callback作为参数分别调用handle方法
- handle方法内通过判断当前state状态为resolved进而执行onResolved方法,
- 如果then中传入了onResolved方法,将value(此时为newValue)传给onResolved作为参数,然后将onResolved的返回值传给resolve方法并调用,至此当前Promise相当于执行结束
- 如果then中没有传入onResolved方法则执行resolve方法,将value(此时为newValue)传给resolve作为参数,至此当前Promise相当于执行结束
总结:
- 通过Promise.prototype.then和Promise.prototype.catch方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。
被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态转变和通知观察者
刚开始看promise源码的时候总不能很好的理解then和resolve函数的运行机理,但是如果你静下心来,反过来根据执行promise时的逻辑来推演,就不难理解了。这里一定要注意的点是:promise里面的then函数仅仅是注册了后续需要执行的代码,真正的执行是在resolve方法里面执行的,理清了这层,再来分析源码会省力的多。
Promise.all
Promise.all(arr)
方法接受一个有多个Promise实例组成的数组对象,返回一个新的Promise
实例,该实例在arr参数内所有子Promise实例
都成为resolved状态时执行父Promise实例的resolve方法,接受的所有子Promise实例结果数组作为参数;当arr参数内任一子Promise实例成为rejected状态,则执行其reject方法,不管其他子Promise实例是否执行完成,失败原因的是第一个rejected的Promise实例
的reject方法的err参数。
通过Promise.all()方法可以实现多个异步函数并发,并在所有异步函数全部执行完成后执行回调函数。由于该方法返回值是一个Promise实例,所以可以使用then方法并进行链式调用。then的参数方法的参数为一个由数组中所有Promise实例的resolve方法的参数组成的数组。
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
setTimeout(resolve, 4000, 'four');
});
var p5 = new Promise((resolve, reject) => {
reject('rejected');
});
//全部成功
Promise.all([p1, p2, p3, p4]).then(values => {
console.log(values);
//在4秒后打印 ['one','two','three','four']
}, reason => {
console.log(reason)
});
//有一个失败即失败
Promise.all([p1, p2, p3, p4, p5]).then(values => {
console.log(values);
}, reason => {
console.log(reason)
//立即打印出 'rejected'
});
//使用catch实现上面的代码
Promise.all([p1, p2, p3, p4, p5]).then(values => {
console.log(values);
}).catch(reason => {
console.log(reason)
//立即打印出 'rejected'
});
Promise.race
Promise.race(arr)
方法接受一个有多个Promise实例组成的数组对象,返回一个新的Promise
实例,该实例在arr参数内任何一个子Promise实例
成为resolved或rejected状态时执行父Promise实例的resolve或reject方法,其他子Promise实例会继续执行,但不会再触发父Promise实例的回调函数。
通过Promise.race()方法可以实现多个异步函数并发,并在任一异步函数执行完成后立即执行回调函数。由于该方法返回值是一个Promise实例,所以可以使用then方法并进行链式调用。then(或catch)的参数方法的参数为子Promise实例的resolve(或reject)方法的参数。
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
setTimeout(resolve, 4000, 'four');
});
var p5 = new Promise((resolve, reject) => {
reject('rejected');
});
//任一成功
Promise.race([p1, p2, p3, p4]).then(values => {
console.log(values);
//在1秒后打印 'one'
}, reason => {
console.log(reason)
});
//任一失败
Promise.race([p1, p2, p3, p4, p5]).then(values => {
console.log(values);
}, reason => {
console.log(reason)
//立即打印出 'rejected'
});
Promise.allSettled
Promise.allSettled(arr)
方法接受一个有多个Promise实例组成的数组对象,在arr参数内所有子Promise实例全部完成(resolve或reject)后,返回
全部子Promise的结果数组。Promise.allSettled不具有race和all方法的短路特性,而是会将每一个子Promise执行完。
通过Promise.allSettled
()方法可以实现多个异步函数并发,并在所有异步函数执行完成后执行回调函数。由于该方法返回值是一个Promise实例,所以可以使用then方法并进行链式调用。then(或catch)的参数方法的参数为子Promise实例的resolve(或reject)方法的参数。
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
setTimeout(reject, 4000, 'four');
});
var p5 = new Promise((resolve, reject) => {
setTimeout(reject, 5000, 'five');
});
//全部完成
Promise.allSettled([p1, p2, p3, p4, p5 ]).then(values => {
console.log(values);
});
/* 在5秒后打印
[
{status: "fulfilled", value: "one"},
{status: "fulfilled", value: "two"},
{status: "fulfilled", value: "three"},
{status: "rejected", reason: "four"},
{status: "rejected", reason: "five"}
]
*/
----------------------------------------------------------------------
原文:https://blog.csdn.net/qq_22844483/article/details/73655738