这篇文章主要介绍了JavaScript怎么实现手写promise的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JavaScript怎么实现手写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、REJECTEDconst 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”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网行业资讯频道。