We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在javascript的世界中,所有代码都是单线程执行的。由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。 最开始我们可以用回调函数来解决这个问题,
function callBack(){ console.log('回调') } setTimeout(callBack, 1000) // 回调
但是随着业务的不断深入,难免会像陷入回调地狱这样的问题。直到后来我们有了Promise来解决这个问题。
promise的基本用法如下: 在实例化一个Promise时,传入一个函数作为参数,该函数接受两个参数,分别为resolve,reject.如解决则会打印数据,如被拒绝则会打印拒绝原因
let p1 = new Promise(function (resolve, reject) { }) p1.then(function (data) { console.log(data) }, function (err) { console.log(err) })
3.状态机制切换 如图所示,状态只能由pengding-->fulfilled,或者由pending-->rejected这样转变。 只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的
function Promise(executor) { this.state = 'pending'; //状态 this.value = undefined; //成功结果 this.reason = undefined; //失败原因 function resolve(value) { } function reject(reason) { } executor(resolve, reject) //立即执行 }
接收一个executor函数,executor函数传入就执行(当我们示例化一个promise时,executor立即执行),执行完同步或异步操作后,调用它的两个参数resolve和reject。其中state保存了promise的状态,包含三个状态:等待态(pending)成功态(resolved)和失败态(rejected)。promise执行成功后的结果由value保存,失败后的原因由reason保存。
function Promise(executor) { this.state = 'pending'; //状态 this.value = undefined; //成功结果 this.reason = undefined; //失败原因 resolve = (value) => { // state改变,resolve调用就会失败 if (this.state === 'pending') { // resolve调用后,state转化为成功态 this.state = 'fulfilled'; // 储存成功的值 this.value = value; } } reject = (reason) => { // state改变,reject调用就会失败 if (this.state === 'pending') { // reject调用后,state转化为失败态 this.state = 'rejected'; // 储存失败的原因 this.reason = reason; } } //如果executor执行报错,直接执行reject try { executor(resolve, reject) } catch (err) { reject(err) // executor出错就直接调用 } }
每一个Promise实例都有一个then方法,接收两个为函数的参数,它用来处理异步返回的结果,它是定义在原型上的方法。
Promise.prototype.then = function (onFulfilled, onRejected) { };
当promise的状态发生了变化,不论成功或失败都会调用then方法,因此then方法里面也会根据不同的状态来判断调用哪一个回调函数。 两个参数的注意事项:
Promise.prototype.then = function (onFulfilled, onRejected) { if (this.state === 'fulfilled') { //判断参数类型,是函数执行之,如果 onFulfilled 不是函数,其必须被忽略 if (typeof onFulfilled === 'function') { onFulfilled(this.value); // 传入成功的值 } } // 如果 onRejected 不是函数,其必须被忽略 if (this.state === 'rejected') { if (typeof onRejected === 'function') { onRejected(this.reason); // 传入失败的原因 } } };
上述把promise的基本功能都实现了,但是还是会存在一个问题,就是promise不支持异步代码,当resolve或reject在setTimeout中实现时,调用then方法时,此时状态仍然是pengding,then方法即没有调用onFulfilled也没有调用onRejected,也就运行没有任何结果。
我们可以参照发布订阅模式,在执行then方法时状态还是状态还是pengding时,把回调函数存储在一个数组中,当状态发生改变时依次从数组中取出执行就好了,首先在类上新增两个Array类型的数组,用于存放回调函数。
function Promise(executor) { this.state = 'pending'; //状态 this.value = undefined; //成功结果 this.reason = undefined; //失败原因 this.onFulfilledFunc = [];//保存成功回调 this.onRejectedFunc = [];//保存失败回调 function resolve(value) { // .... } function reject(reason) { // .... } executor(resolve, reject) //立即执行 }
并修改then方法
Promise.prototype.then = function (onFulfilled, onRejected) { if (this.state === 'pending') { if (typeof onFulfilled === 'function') { this.onFulfilledFunc.push(onFulfilled);//保存回调 } if (typeof onRejected === 'function') { this.onRejectedFunc.push(onRejected);//保存回调 } } if (this.state === 'fulfilled') { //判断参数类型,是函数执行之,如果 onFulfilled 不是函数,其必须被忽略 if (typeof onFulfilled === 'function') { onFulfilled(this.value); // 传入成功的值 } } // 如果 onRejected 不是函数,其必须被忽略 if (this.state === 'rejected') { if (typeof onRejected === 'function') { onRejected(this.reason); // 传入失败的原因 } } };
修改resolve和reject方法:
function Promise(executor) { // 其他代码 function resolve(value) { // state改变,resolve调用就会失败 if (this.state === 'pending') { // resolve调用后,state转化为成功态 this.state = 'fulfilled'; // 储存成功的值 this.value = value; this.onFulfilledFunc.forEach(fn => fn(value)) } } function reject(reason) { // state改变,reject调用就会失败 if (this.state === 'pending') { // reject调用后,state转化为失败态 this.state = 'rejected'; // 储存失败的原因 this.reason = reason; this.onRejectedFunc.forEach(fn => fn(reason)) } } // 其他代码 }
到这里Promise已经支持了异步操作了。
光是实现了异步操作可不行,我们常常用到new Promise().then().then()这样的链式调用来解决回调地狱。 规范如何定义then方法:
Promise.prototype.then = function (onFulfilled, onRejected) { let promise2 = new Promise((resolve, reject) => { // 其他代码 } return promise2; };
接下来就处理根据上一个then方法的返回值来生成新Promise对象.
/** * 解析then返回值与新Promise对象 * @param {Object} promise2 新的Promise对象 * @param {*} x 上一个then的返回值 * @param {Function} resolve promise2的resolve * @param {Function} reject promise2的reject */ function resolvePromise(promise2, x, resolve, reject) { //... }
当then的返回值与新生成的Promise对象为同一个(引用地址相同),状态永远为等待态(pending),再也无法成为resolved或是rejected,程序会死掉,则会抛出TypeError错误
let promise2 = p.then(data => { return promise2; }); // TypeError: Chaining cycle detected for promise #<Promise>
因此需要判断x。
小提示: 为什么取对象上的属性有报错的可能?Promise有很多实现(bluebird,Q等),Promises/A+只是一个规范,大家都按此规范来实现Promise才有可能通用,因此所有出错的可能都要考虑到,假设另一个人实现的Promise对象使用Object.defineProperty()恶意的在取值时抛错,我们可以防止代码出现Bug resolvePromise实现
function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { // 1.x不能等于promise2 reject(new TypeError('Promise发生了循环引用')); } let called; if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // 2. 可能是个对象或是函数 try { let then = x.then;// 3.取出then方法引用 if (typeof then === 'function') { // 此时认为then是一个Promise对象 //then是function,那么执行Promise then.call(x, (y) => { // 5.使用x作为this来调用then方法,即then里面的this指向x if (called) return; called = true; // 6.递归调用,传入y若是Promise对象,继续循环 resolvePromise(promise2, y, resolve, reject); }, (r) => { if (called) return; called = true; reject(r); }); } else { resolve(x); } } catch (e) { // 也属于失败 if (called) return; called = true; reject(e); // 4.取then报错,直接reject } } else { //否则是个普通值 resolve(x); } }
此时链式调用支持已经实现,在相应的地方调用resolvePromise方法即可。
规范还对onFulfilled和onRejected有规定
完善then方法
Promise.prototype.then = function (onFulfilled, onRejected) { let promise2 = new Promise((resolve, reject) => { // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; // onRejected如果不是函数,就忽略onRejected,直接扔出错误 onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; if (this.state === 'pending') { this.onFulfilledFunc.push(() => { // 异步 setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }) this.onRejectedFunc.push(() => { // 异步 setTimeout(() => { try { let x = onRejected(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }) } if (this.state === 'fulfilled') { // 异步 setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } if (this.state === 'rejected') { // 异步 setTimeout(() => { // 如果报错 try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } }) return promise2; };
到这里手写一个Promise已经全部实现了 完整代码
function Promise(executor) { this.state = 'pending'; //状态 this.value = undefined; //成功结果 this.reason = undefined; //失败原因 this.onFulfilledFunc = [];//保存成功回调 this.onRejectedFunc = [];//保存失败回调 resolve = (value) => { // state改变,resolve调用就会失败 if (this.state === 'pending') { // resolve调用后,state转化为成功态 this.state = 'fulfilled'; // 储存成功的值 this.value = value; this.onFulfilledFunc.forEach(fn => fn(value)) } } reject = (reason) => { // state改变,reject调用就会失败 if (this.state === 'pending') { // reject调用后,state转化为失败态 this.state = 'rejected'; // 储存失败的原因 this.reason = reason; this.onRejectedFunc.forEach(fn => fn(reason)) } } //如果executor执行报错,直接执行reject try { executor(resolve, reject) } catch (err) { reject(err) // executor出错就直接调用 } } Promise.prototype.then = function (onFulfilled, onRejected) { let promise2 = new Promise((resolve, reject) => { // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; // onRejected如果不是函数,就忽略onRejected,直接扔出错误 onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; if (this.state === 'pending') { this.onFulfilledFunc.push(() => { // 异步 setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }) this.onRejectedFunc.push(() => { // 异步 setTimeout(() => { try { let x = onRejected(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }) } if (this.state === 'fulfilled') { // 异步 setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } if (this.state === 'rejected') { // 异步 setTimeout(() => { // 如果报错 try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } }) return promise2; }; function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { reject(new TypeError('Promise发生了循环引用')); } let called; if (x !== null && (typeof x === 'object' || typeof x === 'function')) { //可能是个对象或是函数 try { let then = x.then;//取出then方法引用 if (typeof then === 'function') { // 认为then是一个Promise对象 //then是function,那么执行Promise then.call(x, (y) => { // 成功和失败只能调用一个 if (called) return; called = true; //递归调用,传入y若是Promise对象,继续循环 resolvePromise(promise2, y, resolve, reject); }, (r) => { // 成功和失败只能调用一个 if (called) return; called = true; reject(r); }); } else { resolve(x); } } catch (e) { // 也属于失败 if (called) return; called = true; reject(e); } } else { //否则是个普通值 resolve(x); } }
但是只用构造函数实现当然是不够的,我们再用class来实现一个Promise,基本原理同上 class实现
class Promise { constructor(executor) { this.state = 'pending'; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; let resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onResolvedCallbacks.forEach(fn => fn()); } }; let reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn()); } }; try { executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }; let promise2 = new Promise((resolve, reject) => { if (this.state === 'fulfilled') { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }; if (this.state === 'rejected') { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }; if (this.state === '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; } catch(fn) { return this.then(null, fn); } } function resolvePromise(promise2, x, resolve, reject) { if (x === promise2) { return reject(new TypeError('Chaining cycle detected for promise')); } let called; 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); }, err => { if (called) return; called = true; reject(err); }) } else { resolve(x); } } catch (e) { if (called) return; called = true; reject(e); } } else { resolve(x); } } //resolve方法 Promise.resolve = function (val) { return new Promise((resolve, reject) => { resolve(val) }); } //reject方法 Promise.reject = function (val) { return new Promise((resolve, reject) => { reject(val) }); } //race方法 Promise.race = function (promises) { return new Promise((resolve, reject) => { for (let i = 0; i < promises.length; i++) { promises[i].then(resolve, reject) }; }) } //all方法(获取所有的promise,都执行then,把结果放到数组,一起返回) Promise.all = function (promises) { let arr = []; let i = 0; function processData(index, data) { arr[index] = data; i++; if (i == promises.length) { resolve(arr); }; }; return new Promise((resolve, reject) => { for (let i = 0; i < promises.length; i++) { promises[i].then(data => { processData(i, data); }, reject); }; }); }
开源社区提供了一个包用于测试我们的代码:promises-aplus-tests,安装这个包然后运行命令行 promises-aplus-tests [js文件名] 即可验证。别忘了再代码后面加上这一段代码
// 目前是通过他测试 他会测试一个对象 // 语法糖 Promise.defer = Promise.deferred = function () { let dfd = {} dfd.promise = new Promise((resolve,reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }); return dfd; } module.exports = Promise;
参考链接
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
在javascript的世界中,所有代码都是单线程执行的。由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。
最开始我们可以用回调函数来解决这个问题,
但是随着业务的不断深入,难免会像陷入回调地狱这样的问题。直到后来我们有了Promise来解决这个问题。
手写一个promise
promise的基本用法如下: 在实例化一个Promise时,传入一个函数作为参数,该函数接受两个参数,分别为resolve,reject.如解决则会打印数据,如被拒绝则会打印拒绝原因
相关概念
每个promise后面链一个对象该对象包含onfulfiled,onrejected,子promise三个属性,当父promise 状态改变完毕,执行完相应的onfulfiled/onfulfiled的时候呢,拿到子promise,在等待这个子promise状态改变,再执行相应的onfulfiled/onfulfiled。依次循环直到当前promise没有子promise
3.状态机制切换
如图所示,状态只能由pengding-->fulfilled,或者由pending-->rejected这样转变。
只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的
基本结构
接收一个executor函数,executor函数传入就执行(当我们示例化一个promise时,executor立即执行),执行完同步或异步操作后,调用它的两个参数resolve和reject。其中state保存了promise的状态,包含三个状态:等待态(pending)成功态(resolved)和失败态(rejected)。promise执行成功后的结果由value保存,失败后的原因由reason保存。
完善resolve与reject
我们可以这样实现:
实现then方法
每一个Promise实例都有一个then方法,接收两个为函数的参数,它用来处理异步返回的结果,它是定义在原型上的方法。
当promise的状态发生了变化,不论成功或失败都会调用then方法,因此then方法里面也会根据不同的状态来判断调用哪一个回调函数。
两个参数的注意事项:
支持异步实现
上述把promise的基本功能都实现了,但是还是会存在一个问题,就是promise不支持异步代码,当resolve或reject在setTimeout中实现时,调用then方法时,此时状态仍然是pengding,then方法即没有调用onFulfilled也没有调用onRejected,也就运行没有任何结果。
我们可以参照发布订阅模式,在执行then方法时状态还是状态还是pengding时,把回调函数存储在一个数组中,当状态发生改变时依次从数组中取出执行就好了,首先在类上新增两个Array类型的数组,用于存放回调函数。
并修改then方法
修改resolve和reject方法:
到这里Promise已经支持了异步操作了。
链式调用实现
光是实现了异步操作可不行,我们常常用到new Promise().then().then()这样的链式调用来解决回调地狱。
规范如何定义then方法:
总的来说就是不论何时then方法都要返回一个Promise,这样才能调用下一个then方法。我们可以实例化一个promise2返回,将这个promise2返回的值传递到下一个then中。
接下来就处理根据上一个then方法的返回值来生成新Promise对象.
当then的返回值与新生成的Promise对象为同一个(引用地址相同),状态永远为等待态(pending),再也无法成为resolved或是rejected,程序会死掉,则会抛出TypeError错误
因此需要判断x。
此时链式调用支持已经实现,在相应的地方调用resolvePromise方法即可。
最后完善
规范还对onFulfilled和onRejected有规定
完善then方法
到这里手写一个Promise已经全部实现了
完整代码
但是只用构造函数实现当然是不够的,我们再用class来实现一个Promise,基本原理同上
class实现
最终测试
开源社区提供了一个包用于测试我们的代码:promises-aplus-tests,安装这个包然后运行命令行 promises-aplus-tests [js文件名] 即可验证。别忘了再代码后面加上这一段代码
The text was updated successfully, but these errors were encountered: