1. 简介
- Promise是异步编程async的一种解决方案;
- 使用了 Promise 对象之后可以用一种链式调用的方式来组织代码,让代码更加直观,避免回调地狱;
- 兼容低版本:$.Deferred 或 polyfill类库;
1.1 promise 三种状态
- Fulfilled 成功态
- Rejected 失败态
- Pending 初状态
1.2 promise 组成
- 构造函数: new Promise
- 实例方法: .then()、.catch()
- 静态方法: Promise.resolve、Promise.reject、Promise.all、Promise.race;
1.3 promise 特点
- 对象状态只由异步操作结果决定。
- resolve方法会使Promise对象由pendding状态变Fulfilled状态;
- reject方法或者异常会使得Promise对象由pendding状态变为rejected状态。
- Promise状态变化只有这两条路径。
- 对象状态一旦改变,任何时候都能得到这个结果。即状态一旦进入Fulfilled或者Rejected,promise便不再出现状态变化,同时我们再添加回调会立即得到结果。
- 这跟事件不一样,事件是发生后再绑定监听,就监听不到了。
2. 原理
let p = new Promise((resolve, reject) => {
if (Math.random() > 0.5) {
resove();
} else {
reject();
}
});
p.then(() => {
//p的状态被resolved
}, () => {
//p的状态被reject
});
复制代码
1. 调用Promise构造函数,构造实例;
1.1 实例的三种状态:
1) pending (待定)
2) fulfilled (已执行) resolve
3) rejected (已拒绝) reject
只有状态变成已完成(即fulfilled和rejected之一),才能触发对应状态的回调;
2. 构造函数的参数是个函数,此函数接受两个回调函数作为参数,分别为resolve和reject;
3. 在参数函数被执行的过程中:
3.1 如果在其内部调用resolve,会将p的状态变成fulfilled;
3.2 如果在其内部调用reject,会将p的状态变成rejected;
4. .then() 实例调用:
4.1 then中,会为实例注册两种状态的回调函数;
4.2 当实例的状态为fulfilled, 会触发第一个函数;
4.3 当实例的状态为rejected, 会触发第一个函数;
4.4 返回一个promise对象(链式调用)
4.5 在注册状态的回调函数中:
- 使用return: 改变.then返回的promise对象的状态,以及向后面.then注册的状态回调传递数据
- 不使用return: 默认返回的promise对象resolve
5. .catch()
5.1 注册rejected状态的回调函数;
5.2 也是程序出错的回调;
5.3 返回新的promise对象;
6. .resolve()
6.1 返回一个fulfilled状态的promise对象;
7. .reject()
7.1 返回一个rejected状态的promise对象;
复制代码
3. 自己动手实现一个Promise
3.1 基本版
- 思路:
- 在判断状态为pending之后把状态改为相应的值;
- 把对应的value和reason存在self的data属性上面;
- 然后后执行相应的回调函数;
function Promise (executor) {
var self = this;
self.status = 'pending';
self.data = undefined;
self.onResolvedCallback = []; //Promise resolved 回调函数集
self.onRejectedCallback = [];
function resolve (value) {
if (self.status == 'pending') {
self.status = 'fulfiled';
self.data = value;
self.onResolvedCallback.forEach(task => {
task(self.data);
})
}
}
function reject (reason) {
if (self.status == 'pending') {
self.status = 'rejected';
self.data = reason;
self.onRejectedCallback.forEach(task => {
task(self.data);
})
}
}
复制代码
3.2 then方法
链式调用的特点:
-
将then返回值作为下一次成功的回调函数的参数;
-
then 方法是用来注册在这个Promise状态确定后的回调;
-
思路:
- 需要将then写在原型链
prototype
上; - then返回一个
新的Promise
对象;
- 需要将then写在原型链
function myPromise (executor) {
var self = this;
self.status = 'pending';
self.value = undefined;
self.reason = undefined;
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
function resolve (value) {
if (self.status == 'pending') {
self.value = value;
self.status = 'fulfilled';
self.onResolvedCallbacks.forEach(task => task(value));
}
}
function reject (reason) {
if (self.status == 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(task => task(reason));
}
}
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
myPromise.prototype.then = function (onFulfilled, onRejected) {
let self = this;
let promise2;
if (self.status == 'fulfilled') {
return promise2 = new myPromise((resolve, reject) => {
try {
var x = onFulfilled(self.value);
if (x instanceof myPromise) {
// 如果是个promise对象的话,调用then让函数执行;
// 这个resolve,reject分别是promise2的成功和失败的回调;
x.then(resolve, reject); //这就相当于p2中resolve('promise')
} else {
resolve(x);
}
} catch (err) {
reject(err);
}
})
}
if (self.status == 'rejected') {
return promise2 = new myPromise((resolve, reject) => {
try {
var x = onRejected(self.reason);
if (x instanceof myPromise) {
x.then(resolve, reject);
} else {
resolve(x); //注意这里也是resolve,失败的结果也会作为下一次then成功的参数
}
} catch (err) {
reject(err);
}
})
}
if (self.status == 'pending') {
return promise2 = new myPromise((resolve, reject) => {
//因为这里还不知道pending会走向哪里,所以需要存储到回调函数集中
self.onResolvedCallbacks.push(() => {
var x = onFulfilled(self.value);
if (x instanceof myPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
})
self.onRejectedCallbacks.push(() => {
var x = onRejected(self.reason);
if (x instanceof myPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
})
})
}
};
module.exports = myPromise;
复制代码
调用代码
var myPromise = require('./myPromise.js');
var p1 = new myPromise((resolve, reject) => {
/*if (Math.random > 0.5) {
resolve(100);
} else {
reject('错了');
}*/
setTimeout(() => {
resolve(100)
},1000)
});
let p2 = p1.then((data) => {
//return data + 100
return new myPromise((resolve, reject) => {
resolve('这是使用promise对象作为下次执行的结果');
})
});
p2.then((data) => {
console.log(data);
});
复制代码
3.3 Promise值的穿透
解决如下问题:
new Promise(resolve => resolve(8))
.then()
.catch()
.then(value => console.log(value))
复制代码
解决方式:
onResolved = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => throw reason;
复制代码
3.4 catch方法
'.'
catch方法是then(onFulfilled, onRejected) 方法当中 onRejected 函数的一个简单的写法
即Promise.prototype.catch 是 Promise.then(null,reject)的别名
p.catch(function(err) { // todo ... })
等价于
p.then(null, function(err) {//to do ...})
复制代码
.'.
myPromise.prototype.catch = function (func) {
return this.then(null, func);
}
复制代码
3.5 与其它Promise库结合
'.'
- 不同的Promise实现之间需要无缝的可交互;
- 如我们自己实现的Promise,与ES6的Promise,以及其它的Promise库,应该能够无缝相互调用的;
var myPromise = require('./myPromise.js');
var p1 = new myPromise((resolve, reject) => {
resolve(100);
});
let p2 = p1.then((data) => {
return new Promise((resolve, reject) => { //使用原生的Promise
resolve('123');
});
});
p2.then((data) => {
console.log(data); //结果是 Promise { '123' }
});
复制代码
.'.
- 将所有判断返回值是否为Promise对象的地方,改写成:
/* if (x instanceof myPromise) {
x.then(resolve, reject);
} else {
resolve(x);
} */
改写成:
resolvePromise(promise2, x, resolve, reject);
复制代码
实现resolvePromise
思路: 将返回的Promise不断的执行,直到失败或者返回一个普通的值
/**
* promise2 一个新的、自定义的、myPromise对象
* x then方法返回的结果 有可能是普通的值,也有可能是promise对象
* resolve 成功的回调
* reject 失败的回调
* */
function resolvePromise (promise2, x, resolve, reject) {
if (promise2 === x) {
throw new Error('两个对象不能来自同一个引用!!!');
}
if ((x != null) && (typeof x === 'function') || (typeof x === 'object')) {
let then = x.then;
if (typeof then === 'function') {
then.call(x, (value) => {
//这里value也有可能是个promise, 所以需要进行递归
resolvePromise (promise2, value, resolve, reject)
}, (reason) => {
reject(reason);
})
}
} else {
resolve(x);
}
}
复制代码
3.6 Promise.all
- 同样返回一个
新的Promise
对象; - 可以传入多个promise,可以是数组或对象,但必须是可以迭代的;
- 全部执行完成会将结果以数组的方式返回(按传入顺序,按顺序返回结果);
- 如果有一个失败了就失败了,即一旦其中一个promise出错,整个promise会被reject;
var myPromise = require('./myPromise.js');
var p1 = new myPromise((resolve, reject) => {
setTimeout(() => {
// resolve(1);
reject('错了')
},3000)
});
var p2 = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(2);
},1000)
});
myPromise.all([
p1,
p2
]).then((value) => {
console.log(value);
}, (reason) => {
console.log(reason);
});
// 注意:以上异步不是4s后才返回结果,而是3s就返回结果
复制代码
myPromise.all = function (promises) {
return new Promise((resolve, reject) => {
let res = [];
let nIndex = 0; //完成所有promise任务的计数器
let resolved = (idx) => {
return (data) => {
res[idx] = data;
nIndex++;
if (nIndex === promises.length) {
resolve(res); //抛出结果
}
}
};
promises.forEach((promise, idx) => {
promise.then(resolved(idx), (reason) => reject(reason)); //有一个失败,就立即reject
})
});
}
复制代码
3.7 Promise.race
- 同样返回一个
新的Promise
对象 - 可以传入多个promise;
- 参数数组中,一个promise对象状态改变,则这个
新的Promise
对象的状态也转化为该状态
var p1 = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(1);
},5000)
});
var p2 = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(2);
},1000)
});
var p3 = new myPromise((resolve, reject) => {
setTimeout(() => {
reject(3);
},500)
});
myPromise.race([
p1,
p2,
p3
]).then((value) => {
console.log(value);
}, (reason) => {
console.log(reason);
});
复制代码
3.8 Promise.resolve
- 是Promise.resolve(value)的快捷方式;
是以下代码的语法糖
new Promise((resolve) => {
resolve(10)
});
再如:
var obj = {
then: (resolve) => resolve('执行了')
};
var resolved = Promise.resolve(obj);
==> 等价的
var resolved = new Promise((resolve, reject) => {
obj.then(resolve, reject);
});
resolved.then((str) => {
console.log(str)
});
复制代码
- 即将状态立即转换为fulfilled状态,执行onFulfilled函数;
- 同样返回一个新的Promise对象
myPromise.resolve(10).then((value) => {
console.log(value);
})
复制代码
myPromise.resolve = function (value) {
return new myPromise((resolve, reject) => {
if (value && (typeof value === 'function') || (typeof value === 'object')) {
value.then(resolve, reject);
} else {
resolve(value);
}
})
}
复制代码
3.9 Promise.reject
- 同样返回一个新的Promise对象
Promise.reject('错了').catch(function(result) {
console.log(result);
});;
p
复制代码
myPromise.reject = function (value) {
return new myPromise((resolve, reject) => {
reject(value);
});
};
复制代码
应用场景
- 实际工作中使用promise进行了后台数据模拟;
- Promise.all,在不同的接口请求数据然后拼合成自己所需的数据,通常这些接口之间没有关联;
- 网页中预加载20张图片资源,分步加载,一次加载10张,两次完成,怎么控制图片请求的并发,怎样感知当前异步请求是否已完成?