引言
16年时在公司分享过一次promise,犹记得当时是第一次分享,还蛮紧张的,当时分享的主要是promise的使用和基本原理,后来又给无线部门同学分享了一次。
现在回顾想想,其实讲的不是很完美,因为我当时的实现方式类似于简化版q库的实现,考虑的也不全面,也没有完全遵循promise/a+规范。经过这么长一段时间的学习和积累,阐述一下自己新的理解。
Promise出现的起因
在没有promise以前,多个有依赖的异步操作一般写出来会出现嵌套,所谓的回调地域,这种写法在需要协作的项目中不方便维护,异步操作也不能直接捕获异常,需要回调中进行处理,缺点挺多的,然后就开始漫长的优化,出现了q, bluebird,jq中的defer等这些库,后来ES6标准实现了Promise,但是其链式写法还是不美观,为了代码更优雅,可以视觉上同步命令式的书写代码有了TJ大神的co再结合generator似乎完美了,但是为了优雅还要额外引入co库,成本有点大,后来ES7标准干脆直接实现了,就是所谓的async和await语法糖
Promise定义
现在开始切入正题,什么是Promise呢? 简而言之promise代表承诺,专业术语就是代表一个异步操作的最终结果。
代码层面来看的话Promise是一个类,可以用来创建实例,每个实例内部封装一些方法,且维护了一些状态和值,通过使用这些状态、值和方法来将现实流程中的承诺具体代码化表示。
Promise使用
promise主要提供了then,catch,all,race,resolve,reject这几个方法,关于这几个方法怎么使用不在赘述,因为占据文章篇幅长,且很多其它blog重复描述过。推荐阮一峰es6入门中相关api用法解释,详细且全面。
关于具体应用的话,由于在工作中项目基于vue技术栈,所以结合axios时会使用到promise来操作异步,还有就是m站基于pwa,其中Service worker声明周期事件处理中会涉及promise,还有一些就是平时写node工具的时候会用到,用promise封装异步api操作回调,从而将异步api回调逻辑直接放到then方法中进行处理。
Promise的实现
基于Promise/a+规范实现的代码能互相统一,虽然代码形式会有不同,但原理都差不多。
首先Promise构造函数中需要有一些状态和方法,因为执行实例then逻辑的时候需要这些维护好的状态和值,其中着重提醒的就是promise的状态机是单向的,且状态单向不可逆。
状态转变只能是 pending -> fulfilled 或者 pending -> rejected。
//构造函数初始化逻辑
let that = this; //缓存this
//默认状态为pending
that.status = 'pending';
//此变量里放着此promise的结果
that.value = undefined;
//存放的着所有成功的回调函数
that.onResolvedCallbacks = [];
//存放着所有的失败的回调函数
that.onRejectedCallbacks = [];
其中内部resolve和reject逻辑如下,更改状态机状态,触发承诺逻辑执行
function resolve(value) {
//更改状态 执行then注册的成功回调逻辑
if (that.status == 'pending') {
//解决resolve 新Promise这种情况
if(value!=null &&value.then&&typeof value.then == 'function'){
return value.then(resolve,reject);
}
that.status = 'fulfilled';
that.value = value;
that.onResolvedCallbacks.forEach(item=>item(that.value));
}
}
function reject(reason) {
//更改状态 执行then注册的失败回调逻辑或者catch中注册的失败逻辑
if (that.status == 'pending') {
that.status = 'rejected';
that.value = reason;
that.onRejectedCallbacks.forEach(item=>item(that.value));
}
}
上面已经介绍了大致初始化逻辑了,下面着重介绍使用频率最高的then方法,简洁版实现如下所示
PPromise.prototype.then = function (onFulfilled, onReject) {
//成功和失败的逻辑没有传递 会进行值的穿透 传递给下一个then方法
onFulfilled = isFunction(onFulfilled) ?onFulfilled:val =>val;
onReject = isFunction(onReject) ?onReject:reason => {throw reason;}
let self = this,promise2;
switch (self.status){
case 'fulfilled':
promise2 = new Promise((resolve,reject) =>{
let x = onFulfilled(self.value);
if(x instanceof Promise){
//递归执行then逻辑 直到内部then执行,外部promise2被resolve
x.then(resolve,reject)
}else{
resolve(x);
}
});
break
case 'rejected':
promise2 = new Promise((resolve,reject) =>{
let x = onReject(self.value);
if(x instanceof Promise){
x.then(resolve,reject)
}else{
resolve(x);
}
})
break
case 'pending':
promise2 = new Promise((resolve,reject) =>{
self.onResolvedCallbacks.push(function(){
let x = onFulfilled(self.value);
if(x instanceof Promise){
x.then(resolve,reject)
}else{
resolve(x);
}
});
self.onRejectedCallbacks.push(function(){
let x = onReject(self.value);
if(x instanceof Promise){
x.then(resolve,reject)
}else{
resolve(x);
}
});
});
}
return promise2;
}
all方法实现
function sentry(times,cb){
let result = [],count=0;
return function(i,data){
result[i] = data;
if(++count==times){
cb(result);
}
}
}
Promise.all = function(promises){
return new Promise((resolve,reject) => {
//利用闭包机制,目的是为了判断promises是否都执行完
let done = sentry(promises.length,resolve);
for(let i=0;i<promises.length;i++){
promises[i].then(data =>{
done(i,data);
},reject);
}
});
}
resolve实现
Promise.resolve = function(value){
return new Promise(function(resolve){
resolve(value);
});
}
race实现
Promise.race = function(promises){
return new Promise((resolve,reject) =>{
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject);
}
});
}
自己实现的promise源码
异步优雅写法
异步操作经过promisify转化成promise,在结合async实现优雅的写法
let Promise = require('bluebird');
let readFile = Promise.promisify(require('fs').readFile);
async function read() {
let a = await readFile('./1.txt','utf8');
let b = await readFile('./2.txt','utf8');
let c = await readFile('./3.txt','utf8');
console.log(c);
return 'ok';
}
read().then(data => {
console.log(data);
});
总结
任何事物都不是一蹴而就的,都有一个发展过程才逐步变得完美,将自己的学习坐下记录,并加一些个人思考,如果对于本文有任何疑问或错误,欢迎斧正交流。
参考链接
https://promisesaplus.com/
https://segmentfault.com/a/11...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。