promise这个词语,如果从英文的词义上理解大意是‘承诺’的意思,因此你可以认为这个东东要做的事情就是对js代码进行一层封装,保证接下来可以在后面的逻辑中,实时的获取成功或失败的结果,这也正是promise应运而生的意义。
基本用法
const p = new Promise(function(resolve, reject) {...})
我们看下这个promise里面都有啥?
很明显promise的原型上绑定了三个实例方法:分别是
- then
- catch
- finally
接下来我们分别看看三个实例方法的具体用法,这里我只做简单的介绍使用
const p1 = new Promise((res, reject) => {
res('成功了');
});
p1.then(function(res) {
console.log(res, 'success'); // 打印成功了
}, function(err) {
console.log(err, 'fail')
});
const p2 = new Promise((res, reject) => {
reject('失败了');
});
p2.then(function(res) {
console.log(res, 'success');
}, function(err) {
console.log(err, 'fail') // 打印失败了
});
这是ES2015 promise的常规用法,当然我们也可以写成连缀的方式;像下面这样
new Promise((res, reject) => {
...
})
.then(onFulFilled = () => {})
.catch(onRejected = () => {})
.finally(onFinal = () => {})
不管promise最终结果(执行或者成功或者失败)都会走到finally这个钩子函数中。
如果我们需要在异步操作中执行promise的结果,该怎么做呢?该方案可行吗?带着问题看下面代码:
const p3 = new Promise((res, reject) => {
setTimeout(() => {
res('异步调用promise成功了');
}, 2000);
});
p3.then(res => {
console.log(res, '===async_promise');//2秒后 打印 异步调用promise成功了
}).catch(err => {
//这里不会走到这里,因为promise调用resolve成功的钩子函数
})
我们从上面的demo中发现 如果promise的执行结果结束了,最终只会是成功或者失败的一种,不会存在并发的结果,
并且也支持异步调用 +1很赞
像下面这种用法我们可以依次打印 0,1,2,3,4,5
const arr = []
const output = i => new Promise(res => {
setTimeout(()=>{
console.log(i)
res()
}, 1000*i)
})
for (let i=0; i<5; i++) {
arr.push(output(i))
}
Promise.all(arr).then(() => console.log(5));
如果你熟悉es6(ES2016下面都这么简写了)async&await的使用方法话,还可以这么优雅的使用
const sleep = () => new Promise (res => setTimeout(res, 1000))
(async function () {
for (let i=0; i<5; i++) {
await sleep()
console.log(i)
}
})();
依然可以实现上面的要求;是不是现在有点喜欢上了promise了。
说到这里我觉得,是时候了解了解promise内部实现原理了,只有内部原理搞懂了,那么我们就可以游刃有余愉快的撸代码了。。。。。。废话不多说直接开干。
所以第一步 (傻傻想不起来该如何下手啊,看我们之前promise的使用方法开始下手);
既然有new Promise(function(res,rej) {});
那么就该有高配版 new MyPromise(function(res, rej) {});哈哈这个就是我们接下来要定义的类名。
继续!
class MyPromise {} ....完成了,别逗了,继续童鞋们。
这个类要有自己的一点点东西吧,比如上文说的一些实例方法吧
then, catch, finally等等吧。。。。
继续
于是应该是
class MyPromise {
constructor() {
}
then(resolve = () => {}, rej = () => {}) {
}
}
好了不卖关子了,直接一步步撸代码吧,我备注下,只要你认真的看下去应该so easy才对,因为你很优秀!
class MyPromise {
constructor(fn) {
this.state = 'PEDDING'; //我们定义一个状态 表示 promise的进度如何, 初始化就正在进行, 还有成功和失败两个等着
this.value = undefined; //要接收回调的值,初始化就给个undefined吧
//没错最重要的就是这个fn我们实例传进去的函数,初始化要调用一次
fn(this.resolve.bind(this), this.reject.bind(this));
}
resolve(value) {
if (this.state === "PEDDING") {
this.state = "RESOLVED";
this.value = value;
}
}
reject(value) {
if (this.state === "PEDDING") {
this.state = "REJECTED";
this.value = value;
}
}
then(resolve = () => {}, reject = () => {}) {
//resolve,reject是我们自己传递的两个函数参数
if (this.state === "RESOLVED") {
resolve(this.value);
}
if (this.state === "REJECTED") {
reject(this.value);
}
}
}
大功告成。怎么说要测试下吧。
一个简单的promise基本雏形出来了,是不是很有成就感!
顺便来个异步的调用试试看。。。。来就来,走一波代码。
var asyncP = new MyPromise((res, rej) => setTimeout(() => res(123), 2000));
asyncP.then(r => console.log(r, '====r'));
神马情况,居然undefined,头疼。。。。。。
好吧,肯定是有问题的喽,大家可以对上面的代码打个断点试下,原来是state的状态一直是‘PEDDING’状态,所以代码就不会进入成功或者失败的钩子函数中。看来我们需要有一个储存机构来管理这些不确定状态的代码啊,以备后续用到啊。起码我异步调用可以执行吧。废话不多说,直接上代码。
功能相对比较齐全的自定义promise刚出锅。。。。。
class MyPromise {
constructor(fn) {
this.resolvedCallbacks = [];//缓存成功的钩子函数
this.rejectedCallbacks = [];//缓存失败的钩子函数
this.state = 'PEDDING'; //我们定义一个状态 表示 promise的进度如何, 初始化就正在进行, 还有成功和失败两个等着
this.value = undefined; //要接收回调的值,初始化就给个undefined吧
//没错最重要的就是这个fn我们实例传进去的函数,初始化要调用一次
fn(this.resolve.bind(this), this.reject.bind(this));
}
resolve(value) {
if (this.state === "PEDDING") {
this.state = "RESOLVED";
this.value = value;
//缓存中的成功的钩子函数走起来
this.resolvedCallbacks.forEach(cb => cb());
}
}
reject(value) {
if (this.state === "PEDDING") {
this.state = "REJECTED";
this.value = value;
//缓存中的失败的钩子函数走起来
this.rejectedCallbacks.forEach(cb => cb());
}
}
then(resolve = () => {}, reject = () => {}) {
//resolve,reject是我们自己传递的两个函数参数
if (this.state === "PEDDING") {
//如果用箭头函数可以省略this的重赋值
var that = this;
this.resolvedCallbacks.push(function() {
resolve(that.value);
});
this.rejectedCallbacks.push(function() {
reject(that.value);
});
}
if (this.state === "RESOLVED") {
resolve(this.value);
}
if (this.state === "REJECTED") {
reject(this.value);
}
}
}
应该可以秀一波操作了,测试走起,还是之前的异步调用
很明显这次没问题了。2秒后钩子函数正确执行。。。该去喝口水了,口渴。
ES5 promise有一点不是很完美的解决方案,因为假如有一种情况我们需要或者多个promise的最终结果,不管失败还是成功。如果这样该怎么去解决呢?
ES6中针对这一部分有了更好的解决方案。。。我们下一期再谈谈该如何更加优雅的使用promise
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。