最近新型冠状病毒严重,在家也没啥事,写写文章,Promise 前端开发或多或少都有了解或使用到,我也常常用到。 但愿阅读本章能帮助了解Promise究竟是怎么实现的,或多或少能帮你^_^
在javascript开发过程当中,代码是单线程执行的,同步操做,彼此之间不会等待,这能够说是它的优点,可是也有它的弊端,如一些网络操做,浏览器事件,文件等操做等,都必须异步执行,针对这些状况,起初的操做都是使用回调函数实现。javascript
实现方式以下(伪代码):html
function One(callback) { if (success) { callback(err, result); } else { callback(err, null); } } One(function (err, result) { //执行完One函数内的内容,成功的结果回调回来向下执行 })
上述代码只是一层级回调,若是代码复杂后,会出现多层级的回调,代码可读性也会不好,那有没有一种方式,不用考虑里面的内容,直接根据结果成功仍是失败执行下面的代码呢?有的,Promise(承诺),在ES6中对Promise进行了统一的规范前端
Promise可能你们都不陌生,由于Promise规范已经出来好一段时间了,同时Promise也已经归入了ES6,并且高版本的chrome、firefox浏览器都已经原生实现了Promise,只不过和现现在流行的类Promise类库相比少些API。java
Promise规范以下:git
then
方法(能够说,then就是promise的核心),并且then必须返回一个promise,同一个promise的then能够调用屡次,而且回调的执行顺序跟它们被定义时的顺序一致由以上规范就容易就能实现这个类的大体结构github
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Promise</title> </head> <body> <script type="text/javascript"> class Promise { callbacks = []; constructor(fn) { fn(this.resolve.bind(this)); } then(onFulfilled) { this.callbacks.push(onFulfilled); } resolve(value) { this.callbacks.forEach(fn => fn(value)); } } //Promise应用 let p = new Promise(resolve => { setTimeout(() => { resolve('测试'); }, 2000); }) p.then((tip)=>{ console.log('tip1',tip) // tip1,测试 }) p.then((tip)=>{ console.log('tip2',tip) // tip2,测试 }) </script> </body> </html>
这个简单版本大体逻辑是:
实例化Promise时,其类构造函数初始化执行了回调函数,并将绑定了当前实例的resolve方法做为参数回传给回到函数。接着调动Promise对象的then方法, 注册异步操做完成后的回调函数。 当异步操做完成时,调用resolve方法, 该方法执行then方法注册的回调函数。
这里then 方法注册完成时的回到是一个数组, then方法能够屡次调用。注册的函数会在异步操做完成后根据添加的顺序依次执行。chrome
相信仔细的人应该能够看出来,then
方法应该可以链式调用,可是上面的最基础简单的版本显然没法支持链式调用。想让then
方法支持链式调用,其实也是很简单的(若是写过jQuery插件的同窗应该熟悉):数组
// 在上方代码添加 then(onFulfilled) { this.callbacks.push(onFulfilled); return this; //看这里 } // 修改其调用方式 p.then((tip)=>{ console.log('tip1',tip) // tip1,测试 }).then((tip)=>{ console.log('tip2',tip) // tip2,测试 })
首先咱们吧上方代码的栗子中 setTimeout 去掉,在执行一下promise
//Promise应用 let p = new Promise(resolve => { console.log('同步执行1'); resolve('同步执行2'); }).then(tip => { console.log('then1', tip); }).then(tip => { console.log('then2', tip); });
发现只打印了 同步执行1
这显然是不容许的,Promises/A+
规范明确要求回调须要经过异步方式执行,用以保证一致可靠的执行顺序。所以咱们要加入一些处理,保证在resolve
执行以前,then
方法已经注册完全部的回调。咱们能够这样改造下resolve
函数浏览器
// 修改上方代码 resolve(value) { //看这里 setTimeout(() => { this.callbacks.forEach(fn => fn(value)); }); } //经过`setTimeout`机制,将`resolve`中执行回调的逻辑放置到`JS`任务队列末尾,以保证在`resolve`执行时,`then`方法的回调函数已经注册完成.
为了解决上面提到的问题, 咱们须要加入状态机制, 也就是你们熟知的pending, fulfilled, rejected。
Promises/A+ 规范中明确规定了, pending 能够转化为fulfilled或rejected而且只能转化一次。 也就是说若是pending转为fulfiled就不能再转化到rejected。 而且fulfilled 和 rejected状态只能pending转化而来, 二者之间不能互相转换。
// 修改以下 class Promise { callbacks = []; state = 'pending';//增长状态 value = null;//保存结果 constructor(fn) { fn(this.resolve.bind(this)); } then(onFulfilled) { if (this.state === 'pending') { //在resolve以前,跟以前逻辑同样,添加到callbacks中 this.callbacks.push(onFulfilled); } else { //在resolve以后,直接执行回调,返回结果了 onFulfilled(this.value); } return this; } resolve(value) { this.state = 'fulfilled';//改变状态 this.value = value;//保存结果 this.callbacks.forEach(fn => fn(value)); } }
resolve 执行时, 会将状态设置为 fulfilled , 并把 value 的值存起来, 在此以后调用 then 添加的新回调都会当即执行, 直接返回保存的value值
有同窗发现增长了状态的后代码把setTimeout去掉了,缘由是:resolve
执行时,会将状态设置为fulfilled
,在此以后调用then
添加的新回调,都会当即执行
那么这里问题又来了,若是用户再then函数里面注册的仍然是一个Promise
,该如何解决?好比下面
p() .then(()=>{ // 这里返回Promise return new Promise(function (resolve) { resolve(resolve); }); }) .then(function (res) { // res拿到上一个Promise.resolve的值 });
真正的链式调用
真正的链式Promise是指在当前Promise达到fulfilled状态后, 即开始进行下一个Promise(后邻Promise)。 那么咱们如何衔接当前Promise和后邻Promise呢,这个是重点,修改较多,我附一段完整的代码
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Promise</title> </head> <body> <script type="text/javascript"> class Promise { callbacks = []; state = 'pending';//增长状态 value = null;//保存结果 constructor(fn) { fn(this.resolve.bind(this)); } then(onFulfilled) { return new Promise(resolve => { this.handle({ onFulfilled: onFulfilled || null, resolve: resolve }); }); } handle(callback) { if (this.state === 'pending') { this.callbacks.push(callback); return false; } //若是then中没有传递任何东西 if (!callback.onFulfilled) { callback.resolve(this.value); return false; } var ret = callback.onFulfilled(this.value); callback.resolve(ret); } resolve(value) { if (value && (typeof value === 'object' || typeof value === 'function')) { var then = value.then; if (typeof then === 'function') { then.call(value, this.resolve.bind(this)); return; } } this.state = 'fulfilled';//改变状态 this.value = value;//保存结果 this.callbacks.forEach(fn => fn(value)); } } // then中返回Promise let p = new Promise(resolve => { console.log('同步执行1'); resolve('同步执行2'); }).then(tip => { console.log(tip); return new Promise((resolve)=>{ resolve('同步执行3') }) }).then(tip => { console.log(tip); }); </script> </body> </html>
then
方法中,建立并返回了新的Promise
实例,这是串行Promise
的基础,而且支持链式调用。handle
方法是promise
内部的方法。then
方法传入的形参onFulfilled
以及建立新Promise
实例时传入的resolve
均被push
到当前promise
的callbacks
队列中,这是衔接当前promise
和后邻promise
的关键所在(这里必定要好好的分析下handle的做用)。在异常操做失败时,标记其状态为rejected, 并执行注册的失败回调
<!--完整代码--> <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Promise</title> </head> <body> <script type="text/javascript"> class Promise { callbacks = []; state = 'pending';//增长状态 value = null;//保存结果 constructor(fn) { fn(this.resolve.bind(this), this.reject.bind(this)); } then(onFulfilled, onRejected) { return new Promise((resolve, reject) => { this.handle({ onFulfilled: onFulfilled || null, onRejected: onRejected || null, resolve: resolve, reject: reject }); }); } handle(callback) { if (this.state === 'pending') { this.callbacks.push(callback); return; } let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected; if (!cb) {//若是then中没有传递任何东西 cb = this.state === 'fulfilled' ? callback.resolve : callback.reject; cb(this.value); return; } // 这里处理,若是在执行成功回调、失败回调时代码出错怎么办,对于相似异常, 处理也很简单, 可使用try-catch捕获错误, 而后将相应的promise状态设置为rejected状态 let ret; try { ret = cb(this.value); cb = this.state === 'fulfilled' ? callback.resolve : callback.reject; } catch (error) { ret = error; cb = callback.reject } finally { cb(ret); } } resolve(value) { if (value && (typeof value === 'object' || typeof value === 'function')) { var then = value.then; if (typeof then === 'function') { then.call(value, this.resolve.bind(this), this.reject.bind(this)); return; } } this.state = 'fulfilled';//改变状态 this.value = value;//保存结果 this.callbacks.forEach(callback => this.handle(callback)); } reject(error) { this.state = 'rejected'; this.value = error; this.callbacks.forEach(callback => this.handle(callback)); } } //Promise应用 let p = new Promise(resolve => { console.log('同步执行1'); resolve('同步执行2'); }).then(tip => { return new Promise((resolve,reject)=>{ // 作个随机数控制resolve或者reject的调用 if(parseInt(Math.random()*10) > 4){ resolve(tip+' 成功') }else{ reject(tip+' 失败') } }) }).then(result => { console.log(result); }, error => { console.log(error); }); </script> </body> </html>
promise 里面的 then 函数仅仅是注册了后续须要执行的回调函数,同时返回一个新的Promise对象,以延续链式调用,真正的逻辑是在handle里面
对于内部 pending 、fulfilled 和 rejected 的状态转变,经过 handler 触发 resolve 和 reject 方法,而后更改state状态值
MPromise
分层解析 Promise 的实现原理
Promise 原理解析 (渐进实例详解)