摘要:一个后可以通过方法,指定和时的回调函数。构造函数内部要有一个值,用来保存上次执行的结果值,如果报错,则保存的是异常信息。因为是一个构造函数,使用的写法,首先想到的就是有显式声明的。
Javascript语言的执行环境是"单线程"(single thread)。所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
异步模式"编程的4种方法:回调函数、事件监听、发布/订阅、Promises对象。
还有generator、async/await.
本文尝试说一下对Promise的理解及如何实现。
1.Promise原理
promise对象有三种状态,pending、fulfilled和rejected。promise对象内部保存一个需要执行一段时间的异步操作,当异步操作执行结束后可以调用resolve或reject方法,来改变promise对象的状态,状态一旦改变就不能再变。new一个promise后可以通过then方法,指定resolved和rejected时的回调函数。下面是我们日常使用Promise的代码逻辑。
let p = new Promise((resolve,reject)=>{
$.ajax({ url: "../www/data.txt", dataType: "json", success(data){ resolve(data); }, error(err){ reject(err); } }); }); p.then(function(data){ alert("success"+data); },function(err){ alert("failed"); })
结合Promise A+规范,我们就可以分析一下我们要实现一个什么东西:
实现一个状态机,有三个状态,pending、fulfilled、rejected,状态之间的转化只能是pending->fulfilled、pending->rejected,状态变化不可逆。
实现一个then方法,可以用来设置成功和失败的回调
then方法要能被调用多次,所以then方法需要每次返回一个新的promise对象,这样才能支持链式调用。
构造函数内部要有一个value值,用来保存上次执行的结果值,如果报错,则保存的是异常信息。
2.实现原理
2.1实现状态机
那我们现在就按照上面提到的原理和规范来实现这个Promise构造函数。
class myPromise {
constructor(executor) { this.status = PENDING; this.value = ""; this.Resolve = this.resolve.bind(this); this.Reject = this.reject.bind(this); this.then= this.then.bind(this); executor(this.Resolve, this.Reject); } resolve(value) { if (this.status === PENDING) { this.value = value; this.status = FULFILLED; } } reject(value) { if (this.status === PENDING) { this.value = value; this.status = REJECTED; } } then(onfulfilled, onrejected) { if (this.status === FULFILLED) { onfulfilled(this.value); } if (this.status === REJECTED) { onrejected(this.value); } } } const PENDING = "pending"; const FULFILLED = "fulfilled"; const REJECTED = "rejected"; const test = new myPromise((resolve, reject) => { resolve(100); }); test.then((data) => { console.log(data); }, (data) => { });
因为Promise是一个构造函数,使用ES6的写法,首先想到的就是有显式constructor声明的class。上面就是我们用class的实现,可以看到这样我们就实现了这个状态机,有status, value两个属性和resolve, reject, then三个函数;同时它有pending, fulfilled和rejected三个状态,其中pending就可以切换为fulfilled或者rejected两种。
运行一下,输出了100,但是现在其实不是一个异步处理方案,代码先运行了resolve(100)然后又运行了then函数,其实对于异步的情况没有处理,不信的话就给resolve加一个setTimeout,好了,代码又没有输出了。
2.2 实现异步设置状态
作为一个异步处理的函数,在使用的时候,我们肯定是会先设置好不同异步返回后的处理逻辑(即then的成功、失败调用函数),然后安心等待异步执行,最后再异步结束后,系统会自动根据我们的逻辑选择调用不同回调函数。换句话说,then函数要对status为pending的状态进行处理。处理的原理是设置两个数组,在pending状态下分别保存成功和失败回调函数,当状态改变后,再根据状态去调用数组中保存的回调函数。
class myPromise {
constructor (executor) {
this.status = PENDING; this.value = ""; this.onfulfilledArr = []; this.onrejectedArr = []; this.resolve = this.resolve.bind(this); this.reject = this.reject.bind(this); this.then = this.then.bind(this); executor(this.resolve, this.reject);
}
resolve (value) {
if (this.status === PENDING) { this.value = value; this.onfulfilledArr.forEach(item => { item(this.value); }) this.status = FULFILLED; }
}
reject (value) {
if (this.status === PENDING) { this.value = value; this.onrejectedArr.forEach(item => { item(this.value); }) this.status = REJECTED; }
}
then (onfulfilled, onrejected) {
if (this.status === FULFILLED) { onfulfilled(this.value); } if (this.status === REJECTED) { onrejected(this.value); } if (this.status === PENDING) { this.onfulfilledArr.push(onfulfilled); this.onrejectedArr.push(onrejected); }
}
}
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
const test = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(100);
}, 2000)
});
test.then((data) => {
console.log(data);
},(data) => {});
可以这样理解
new myPromise 有异步代码
setTimeout(() => {
resolve(100);
}, 2000)
js是单线程的,这个时候会先把这个任务添加到定时触发器线程中去(计时完毕后添加到事件队列中,等待js引擎空闲后执行),先去执行下面的同步代码
test.then((data) => {
console.log(data);
},(data) => {});
完成输出及状态改变。
但是Promise的一大特点就是可以链式调用,即test.then(success, fail).then(success, fail)...这就需要then返回一个新的Promise对象,而我们的程序现在明显的是不支持的。那么继续改一下。
2.3 实现链式调用
再观察一下链式调用,如果成功和失败的函数中有返回值,这个值要作为参数传给下个then函数的成功或失败回调。所以我们要在返回的new Promise中调用相应的函数。
class myPromise {
constructor (executor) {
this.status = PENDING; this.value = ""; this.onfulfilledArr = []; this.onrejectedArr = []; this.resolve = this.resolve.bind(this); this.reject = this.reject.bind(this); this.then = this.then.bind(this); executor(this.resolve, this.reject);
}
resolve (value) {
if (this.status === PENDING) { this.value = value; this.onfulfilledArr.forEach(item => { item(this.value); }) this.status = FULFILLED; }
}
reject (value) {
if (this.status === PENDING) { this.value = value; this.onrejectedArr.forEach(item => { item(this.value); }) this.status = REJECTED; }
}
then (onfulfilled, onrejected) {
if (this.status === FULFILLED) { const res = onfulfilled(this.value); return new Promise(function(resolve, reject) { resolve(res); }) } if (this.status === REJECTED) { const res = onrejected(this.value); return new Promise(function(resolve, reject) { reject(res); }) } if (this.status === PENDING) { const self = this; return new Promise(function(resolve, reject) { self.onfulfilledArr.push(() => { const res = onfulfilled(self.value) resolve(res); }); self.onrejectedArr.push(() => { const res = onrejected(self.value) reject(res); }); }) }
}
}
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
const test = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(100);
}, 2000)
});
test.then((data) => {
console.log(data);
return data + 5;
},(data) => {})
.then((data) => {
console.log(data)
},(data) => {});
再运行一下,输出100,105。好了,一个简单的Promise就实现好了。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/113705.html
摘要:一个后可以通过方法,指定和时的回调函数。构造函数内部要有一个值,用来保存上次执行的结果值,如果报错,则保存的是异常信息。因为是一个构造函数,使用的写法,首先想到的就是有显式声明的。 showImg(https://segmentfault.com/img/bVbffEu?w=530&h=253); Javascript语言的执行环境是单线程(single thread)。所谓单线程,就...
摘要:意味着操作成功完成。状态的对象可能触发状态并传递一个值给相应的状态处理方法,也可能触发失败状态并传递失败信息。测试用例测试用例方法返回一个带有拒绝原因参数的对象。 Promise基本用法 Promise 对象是一个代理对象,被代理的值在Promise对象创建时可能是未知的。 它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值...
摘要:它之后能够被使用到很多场景中其他处理请求和响应的方式,甚至任何需要生成自己的响应的方式。总结到这里都讲完了,其实没什么难度,主要是自己项目中遇到了,但是中没有这个方法啊。所以就想着实现了一个,因为其他的方法也都封装,不差这一个了。 Fetch 提供了对 Request 和 Response (以及其他与网络请求有关的)对象通用的定义。它之后能够被使用到很多场景中:service wor...
摘要:等待的基本语法该关键字的的意思就是让编译器等待并返回结果。这里并不会占用资源,因为引擎可以同时执行其他任务其他脚本或处理事件。接下来,我们写一个火箭发射场景的小例子不是真的发射火箭 本文由云+社区发表 本篇文章,小编将和大家一起学习异步编程的未来——async/await,它会打破你对上篇文章Promise的认知,竟然异步代码还能这么写! 但是别太得意,你需要深入理解Promise后,...
阅读 2531·2021-09-22 15:25
阅读 2934·2021-09-14 18:03
阅读 1157·2021-09-09 09:33
阅读 1668·2021-09-07 09:59
阅读 2867·2021-07-29 13:50
阅读 1472·2019-08-30 15:44
阅读 1681·2019-08-29 16:22
阅读 1259·2019-08-29 12:49