摘要: 在好久好久之前,Promise
尚未来到这个世上。那时森林里的有这样一群攻城狮,他们饱受回调地狱(回调金字塔)的摧残,苦不堪言。直到有一天,一位不肯意留下姓名的特工横空出世,将他们从回调地狱中解救了出来,代号Promise
。自此,不少人都踏上了寻找Promise
的道路,我亦如此... node
友情提醒: 本文使用ES6实现的Promise
,不会的童鞋们请自行脑补!What?这位同窗你居然不知道ES6,好的,放学了请不要走,咱们单独交流一下......数组
就拿fs
(node的核心包)来讲吧,假如咱们须要同时请求a.txt
和b.txt
中的数据,而后对数据进行操做。这种需求在咱们的开发中也常常遇到哦!promise
let fs = require('fs');
let arr = [];
fs.readFile('a.txt','utf8',function(err,data){
arr.push(data);
fs.readFile('b.txt','utf8',function(err,data){
arr.push(data);
// 若是须要把更多的文件数据,那滋味不敢想象
console.log(arr);
})
})
复制代码
let fs = require('fs');
function read(url,coding){ // 首先咱们对fs.readFile()进行promise封装
return new Promise((resolve,reject)=>{
fs.readFile(url,coding,function(err,data){
if(err) reject(err);
resolve(data);
})
})
}
Promise.all([read('a.txt','utf8'),read('b.txt','utf8')]).then(data=>{
// 这里咱们就能够直接操做请求到的两个文件的数据了,Promise还很贴心的返回了一个数组
console.log(data);
})
复制代码
相比较之下,Promise
和回调地狱的战争起初就不是一个等级的呀,回调地狱听起来强大,但实则一点不经揍啊!Promise
此时的心里应该是这样的: bash
看到这里,相信你们都很想知道Promise的核心实现是什么?接下来,请小伙伴们不要闭眼,看这里,看这里!我便说说我是如何在寻找Promise
的道路上一条道走到黑的。(这标题起的,笑出猪叫声)异步
起初,我发现Promise是能够被new的,说明Promise 的出身是一个类啊,这但是一条颇有价值的线索啊。(你们都知道,还用你说)函数
class Promise {
constructor(executor) { // executor是new Promise的参数
this.status = 'pending'; // 保存状态
this.reason = undefined; // 失败的缘由
this.value = undefined; // 成功的结果
let resolve = (value)=> {
if(this.status === 'pending'){
this.status = 'resolved';
this.value = value;
}
}
let reject = (reason)=>{
if(this.status === 'pending'){
this.status = 'rejected';
this.reason = reason;
}
}
try {
executor(resolve, reject); // 执行器
} catch (e) {
reject(e);
}
}
// 定义实例上的then方法,then调用的时候都是异步调用的
then(onFulfilled, onRejected) {
if(this.status === 'resolved'){ // status状态改变时执行onFulFilled
onFulfilled(this.value);
}
if(this.status === 'rejected'){ // status状态改变时执行onFulFilled
onRejected(this.reason);
}
}
}
复制代码
这怎么仅仅一条线索就写出来这么东东呀,真让人摸不着头脑!别急,听我慢慢道来:ui
executor
:执行器,默认是new的时候就自动执行,executor的执行是同步的,为何要try一下呢,executor执行时若是throw new Error('error')
,直接走rejectresolve, reject
:定义了executor
的resolve
成功的回调函数和reject
失败的回调函数两个参数reason,value
:分别表明了成功返回的值和失败的缘由status
:保存了Promise
的三种状态pending
(等待态),fulfilled
(成功态),rejected
(失败态)
promise
的状态处于pending
时,它能够过渡到fulfilled
或者ejected
fulfilled
时或者rejected
时,不能再过渡到其余任何状态then
函数: 因Promise
是能够链式调用的,说明then
函数是定义在Promise
类的原型Prototype
上的。这样咱们就成功处理了同步状况下的
Promise
,是否是以为本身已经追寻到Promise
的终极力量了呢。(抽根烟,平复下躁动的心情)this
在咱们平时的开发中,每每异步代码比较多,异步执行须要,然而Promise
的executor
执行器又是同步执行的,它不等咱们怎么办呢,好着急有木有。 咱们在上面代码的基础上新增以下几行代码:url
class Promise {
constructor(executor) {
this.onResolvedCallbacks = []; // 保存成功的回调
this.onRejectedCallbacks = []; // 保存失败的回调
let resolve = (value)=> {
if(this.status === 'pending'){
this.status = 'resolved';
this.value = value;
this.onResolvedCallbacks.forEach(fn=>fn());
}
}
let reject = (reason)=>{
if(this.status === 'pending'){
this.status = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn=>fn());
}
}
}
then(onFulfilled, onRejected) {
if(this.status === 'pending'){
this.onResolvedCallbacks.push(()=>{
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason);
});
});
}
}
}
复制代码
当出现异步代码时,status
的状态仍是pending
,咱们能够先把then
函数中成功和失败的回调保存下来,等到异步代码执行完成后,status
的状态改变了,咱们再去依次执行保存下来的回调函数。spa
看到这里,若是以为本身已经基本掌握Promise
的实现,只能说尔等对Promise
的核心力量一无所知。(别废话,赶忙写)好的,各位大佬!
在开始实现以前呢,咱们先来看一下以下代码:
// 这里的Promise是ES6封装好的,并非咱们本身实现的
let promise = new Promise((resolve,reject)=>{
resolve('123');
})
let promise2 = promise.then((data)=>{
throw new Error('error');
})
promise2.then((data)=>{
console.log(data);
},(err)=>{
console.log(err); // 这里输出了error
})
复制代码
上面代码说明
then
函数执行后返回的promise2
实例并非promise
实例,说明返回值不是this
,由于promise
不能即调用成功后不能再走失败,因此then
函数执行后返回的promise2
是一个新的promise
实例。(跟jQuery的链式调用不同哦)
Promise
的constructor
的代码不须要改变,只须要对then
函数进行再次封装:
then(onFulfilled, onRejected) {
// onFulfilled和onRejected可能没传
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value=>value;
onRejected = typeof onRejected === 'function' ? onRejected : (err)=>{throw err};
let promise2; // 须要每次调用then方法时,都须要返回新的promise
promise2 = new Promise((resolve, reject)=>{
if(this.status === 'resolved'){
setTimeout(()=>{
try {
let x = onFulfilled(this.value);
// 执行完当前回调后返回的结果可能仍是个promise
resolvePromise(promise2,x,resolve, reject);
} catch (e) {
reject(e);
}
},0)
}
if(this.status === 'rejected'){
setTimeout(()=>{
try {
let x = onRejected(this.reason);
resolvePromise(promise2,x,resolve, reject);
} catch (e) {
reject(e);
}
},0)
}
if(this.status === 'pending'){
this.onResolvedCallbacks.push(()=>{
setTimeout(()=>{
try {
let x = onFulfilled(this.value);
resolvePromise(promise2,x,resolve, reject);
} catch (e) {
reject(e);
}
},0)
});
this.onRejectedCallbacks.push(()=>{
setTimeout(()=>{
try {
let x = onRejected(this.reason);
resolvePromise(promise2,x,resolve, reject);
} catch (e) {
reject(e);
}
},0)
});
}
})
return promise2;
}
复制代码
onFulfilled,onRejected
:当没有传的时候,须要作的处理promise2
:then
函数的返回值是一个新的promisesetTimeout
:Promise/A+规范(规范)要求then
函数必须是异步的,固然原生的Promise实现并非用的setTimeout,而是一个微任务resolvePromise
:封装resolvePromise
方法,当then函数中的成功或者失败函数返回值x可能仍是个promise定义的resolvePromise方法:
let resolvePromise = (promise2,x,resolve, reject)=>{
let called;
// promise2和函数的返回值是同一个
if(promise2 === x){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
if(x!==null && (typeof x === 'object' || typeof x === 'function')){
try {
let then = x.then;
if(typeof then === 'function'){
then.call(x,(y)=>{
if(called) return;
called = true;
resolvePromise(promise2,y,resolve, reject);// 递归处理,直到y是一个普通值
},(err)=>{
if(called) return;
called = true;
reject(err);
})
}else{ // then若是是一个常量
if(called) return;
called = true;
resolve(x);
}
} catch (e) {
if(called) return;
called = true;
reject(e);
}
}else{ // x若是是一个常量
if(called) return;
called = true;
resolve(x);
}
}
复制代码
promise2
(then函数的返回值,是一个新的promise) x
(then中成功后者失败函数的返回值)resolve
(promise2的resolve)reject
(promise2的reject)called
: 加了called
判断,防止屡次调用,由于这里的逻辑不仅仅是本身的,还有别人的,别人的promise
可能即会调用成功也会调用失败let then = x.then
:x
可能仍是一个promise
,那么就让这个Promise
执行至此,咱们终于追寻到了promise
的核心力量所在。来,让咱们小小的庆贺一下:
固然,咱们已经初步了解了promise
的核心力量,在咱们开发的过程当中,除了then方法,也会使用它的一些其余经常使用的方法,就像一位身经百战的特工,你除了会用刀,还要会用枪不是。咱们在Promise类上定义它们:
static resolve(value){
return new Promise((resolve,reject)=>{
resolve(value);
})
}
static reject(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
static all(promises){
return new Promise((resolve,reject)=>{
let arr = [];
let i = 0;
let processData = (index,data)=>{
arr[index] = data;
if(++i === promises.length){
resolve(arr);
}
}
for(let i = 0; i< promises.length; i++){
promises[i].then(data=>{
processData(i,data);
},reject);
}
})
}
static race(promises){
return new Promise((resolve,reject)=>{
for(let i = 0; i< promises.length; i++){
promises[i].then(resolve,reject);
}
})
}
catch(onRejected){
return this.then(null,onRejected);
}
复制代码
相信resolve,reject,all,race这四个类上的方法和catch这个原型的方法你们都已经很熟悉了,我就不过多的啰嗦了。
由于,我实在是编不下去了,我还有更重要的事情要去作:
结语: 花了好久写的这篇文章,若是这篇文章令你或多或少有些收获,请不要吝啬你的赞美(点个赞再走吗,小哥哥小姐姐),若是有写的不对的地方,也但愿各位大佬能不吝指教,万分感谢!原创文章,转载请注明出处!