JavaScript实现手写promise的示例代码
作者:Lvan的前端笔记
promise 作为前端开发中常用的函数,解决了 js 处理异步时回调地狱的问题,大家应该也不陌生了,今天来学习一下 promise 的实现过程吧
背景
promise 作为前端开发中常用的函数,解决了 js 处理异步时回调地狱的问题,大家应该也不陌生了,今天来学习一下 promise 的实现过程,这样可以加(面)深(试)理(要)解(考)。
需求
我们先来总结一下 promise 的特性:
使用:
const p1 = new Promise((resolve, reject) => { console.log('1'); resolve('成功了'); }) console.log("2"); const p2 = p1.then(data => { console.log('3') throw new Error('失败了') }) const p3 = p2.then(data => { console.log('success', data) }, err => { console.log('4', err) })
以上的示例可以看出 promise 的一些特点,也就是我们本次要做的事情:
- 在调用 Promise 时,会返回一个 Promise 对象,包含了一些熟悉和方法(all/resolve/reject/then…)
- 构建 Promise 对象时,需要传入一个 executor 函数,接受两个参数,分别是resolve和reject,Promise 的主要业务流程都在 executor 函数中执行。
- 如果运行在 excutor 函数中的业务执行成功了,会调用 resolve 函数;如果执行失败了,则调用 reject 函数。
- promise 有三个状态:pending,fulfilled,rejected,默认是 pending。只能从pending到rejected, 或者从pending到fulfilled,状态一旦确认,就不会再改变;
- promise 有一个then方法,接收两个参数,分别是成功的回调 onFulfilled, 和失败的回调 onRejected。
// 三个状态:PENDING、FULFILLED、REJECTED const PENDING = "PENDING"; const FULFILLED = "FULFILLED"; const REJECTED = "REJECTED"; class Promise { constructor(executor) { // 默认状态为 PENDING this.status = PENDING; // 存放成功状态的值,默认为 undefined this.value = undefined; // 存放失败状态的值,默认为 undefined this.reason = undefined; // 调用此方法就是成功 let resolve = (value) => { // 状态为 PENDING 时才可以更新状态,防止 executor 中调用了两次 resovle/reject 方法 if (this.status === PENDING) { this.status = FULFILLED; this.value = value; } }; // 调用此方法就是失败 let reject = (reason) => { // 状态为 PENDING 时才可以更新状态,防止 executor 中调用了两次 resovle/reject 方法 if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; } }; try { // 立即执行,将 resolve 和 reject 函数传给使用者 executor(resolve, reject); } catch (error) { // 发生异常时执行失败逻辑 reject(error); } } // 包含一个 then 方法,并接收两个参数 onFulfilled、onRejected then(onFulfilled, onRejected) { if (this.status === FULFILLED) { onFulfilled(this.value); } if (this.status === REJECTED) { onRejected(this.reason); } } }
调用一下:
const promise = new Promise((resolve, reject) => { resolve('成功'); }).then( (data) => { console.log('success', data) }, (err) => { console.log('faild', err) } )
这个时候我们很开心,但是别开心的太早,promise 是为了处理异步任务的,我们来试试异步任务好不好使:
const promise = new Promise((resolve, reject) => { // 传入一个异步操作 setTimeout(() => { resolve('成功'); },1000); }).then( (data) => { console.log('success', data) }, (err) => { console.log('faild', err) } )
发现没有任何输出,我们来分析一下为什么:
new Promise 执行的时候,这时候异步任务开始了,接下来直接执行 .then 函数,then函数里的状态目前还是 padding,所以就什么也没执行。
那么我们想想应该怎么改呢?
我们想要做的就是,当异步函数执行完后,也就是触发 resolve 的时候,再去触发 .then 函数执行并把 resolve 的参数传给 then。
这就是典型的发布订阅的模式,可以看我这篇文章。
const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; class Promise { constructor(executor) { this.status = PENDING; this.value = undefined; this.reason = undefined; // 存放成功的回调 this.onResolvedCallbacks = []; // 存放失败的回调 this.onRejectedCallbacks= []; let resolve = (value) => { if(this.status === PENDING) { this.status = FULFILLED; 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()); } } try { executor(resolve,reject) } catch (error) { reject(error) } } then(onFulfilled, onRejected) { if (this.status === FULFILLED) { onFulfilled(this.value) } if (this.status === REJECTED) { onRejected(this.reason) } if (this.status === PENDING) { // 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行 this.onResolvedCallbacks.push(() => { onFulfilled(this.value) }); // 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行 this.onRejectedCallbacks.push(()=> { onRejected(this.reason); }) } } }
这下再进行测试:
const promise = new Promise((resolve, reject) => { setTimeout(() => { resolve('成功'); },1000); }).then( (data) => { console.log('success', data) }, (err) => { console.log('faild', err) } )
1s 后输出了:
说明成功啦
then的链式调用
这里就不分析细节了,大体思路就是每次 .then 的时候重新创建一个 promise 对象并返回 promise,这样下一个 then 就能拿到前一个 then 返回的 promise 了。
const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; const resolvePromise = (promise2, x, resolve, reject) => { // 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise Promise/A+ 2.3.1 if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } // Promise/A+ 2.3.3.3.3 只能调用一次 let called; // 后续的条件要严格判断 保证代码能和别的库一起使用 if ((typeof x === 'object' && x != null) || typeof x === 'function') { try { // 为了判断 resolve 过的就不用再 reject 了(比如 reject 和 resolve 同时调用的时候) Promise/A+ 2.3.3.1 let then = x.then; if (typeof then === 'function') { // 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.defineProperty Promise/A+ 2.3.3.3 then.call(x, y => { // 根据 promise 的状态决定是成功还是失败 if (called) return; called = true; // 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1 resolvePromise(promise2, y, resolve, reject); }, r => { // 只要失败就失败 Promise/A+ 2.3.3.3.2 if (called) return; called = true; reject(r); }); } else { // 如果 x.then 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.3.4 resolve(x); } } catch (e) { // Promise/A+ 2.3.3.2 if (called) return; called = true; reject(e) } } else { // 如果 x 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.4 resolve(x) } } class Promise { constructor(executor) { this.status = PENDING; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks= []; let resolve = (value) => { if(this.status === PENDING) { this.status = FULFILLED; 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()); } } try { executor(resolve,reject) } catch (error) { reject(error) } } then(onFulfilled, onRejected) { //解决 onFufilled,onRejected 没有传值的问题 //Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v; //因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后 then 的 resolve 中捕获 onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; // 每次调用 then 都返回一个新的 promise Promise/A+ 2.2.7 let promise2 = new Promise((resolve, reject) => { if (this.status === FULFILLED) { //Promise/A+ 2.2.2 //Promise/A+ 2.2.4 --- setTimeout setTimeout(() => { try { //Promise/A+ 2.2.7.1 let x = onFulfilled(this.value); // x可能是一个proimise resolvePromise(promise2, x, resolve, reject); } catch (e) { //Promise/A+ 2.2.7.2 reject(e) } }, 0); } if (this.status === REJECTED) { //Promise/A+ 2.2.3 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; } }
Promise.all
核心就是有一个失败则失败,全成功才进行 resolve
Promise.all = function(values) { if (!Array.isArray(values)) { const type = typeof values; return new TypeError(`TypeError: ${type} ${values} is not iterable`) } return new Promise((resolve, reject) => { let resultArr = []; let orderIndex = 0; const processResultByKey = (value, index) => { resultArr[index] = value; if (++orderIndex === values.length) { resolve(resultArr) } } for (let i = 0; i < values.length; i++) { let value = values[i]; if (value && typeof value.then === 'function') { value.then((value) => { processResultByKey(value, i); }, reject); } else { processResultByKey(value, i); } } }); }
到此这篇关于JavaScript实现手写promise的示例代码的文章就介绍到这了,更多相关JavaScript手写promise内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!