2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 用es5实现es6的promise 彻底搞懂promise的原理

用es5实现es6的promise 彻底搞懂promise的原理

时间:2021-08-15 18:14:54

相关推荐

用es5实现es6的promise 彻底搞懂promise的原理

1.promise的含义

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

2.小试promise用法:

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

var p=new Promise(function(resolve,rejcet){setTimeout(function(){if(true){//异步操作成功resolve('success');}else{//异步操作失败rejcet('failure');}},1000);});p.then(function(value){//成功的回调console.log(value);},function(error){//失败的回调console.log(error);});复制代码

从demo中可以看到,在ES6中Promise对象是一个构造函数,用来生成Promise实例。并且Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

3.手写一个promise,彻底掌握它的原理

首先,我们先搭建好代码的骨架:

function Promise(callback) {var self = this //promise实例self.status = 'PENDING' // Promise当前的状态self.data = undefined // Promise的值self.onResolvedCallback = [] // Promise resolve时的回调函数集self.onRejectedCallback = [] // Promise reject时的回调函数集callback(resolve, reject) // 执行executor并传入相应的参数function resolve(value){}function reject(error){}}// 添加我们的then方法Promise.prototype.then=function(){}复制代码

我们先创建一个Promise构造函数,并传入一个回调函数callback,callback里面传入两个函数作为参数,一个是resove,一个是reject。并在Promise的原型上加入我们的then方法。框架搭好了,接下来我们来一点点的完善框架里面的内容,可以这么说,把resolve,reject和then补充完,基本可以说就是把Promise完成了。

然后紧接着,我们完善一下resolve和reject:

function Promise(callback) {var self = thisself.status = 'PENDING' // Promise当前的状态self.data = undefined // Promise的值self.onResolvedCallback = [] // Promise resolve时的回调函数集self.onRejectedCallback = [] // Promise reject时的回调函数集callback(resolve, reject) // 执行executor并传入相应的参数function resolve(value){if(self.status=='PENDING'){self.status=='FULFILLED';self.data=value;// 依次执行成功之后的函数栈for(var i = 0; i < self.onResolvedCallback.length; i++) {self.onResolvedCallback[i](value)}}}function rejecte(error){if (self.status === 'PENDING') {self.status = 'REJECTED'self.data = error;// 依次执行失败之后的函数栈for(var i = 0; i < self.onRejectedCallback.length; i++) {self.onRejectedCallback[i](error)}}}}复制代码

接下来我们实现我们的then方法:

then方法是Promise的核心,因此这里会花比较大的篇幅去介绍then:

一个promise的then接受两个参数:

promise.then(onFulfilled, onRejected)复制代码

onFulfilledonRejected都是可选参数。

如果onFulfilled不是函数,其必须被忽略如果onRejected不是函数,其必须被忽略

onFulfilled 特性

如果onFulfilled是函数:

promise执行结束后其必须被调用,其第一个参数为promise的终值,也就是resolve传过来的值在promise执行结束前其不可被调用其调用次数不可超过一次

onRejected 特性

如果onRejected是函数:

promise被拒绝执行后其必须被调用,其第一个参数为promise的据因,也就是reject传过来的值在promise被拒绝执行前其不可被调用其调用次数不可超过一次

调用时机

onFulfilledonRejected只有在执行环境堆栈仅包含平台代码时才可被调用(平台代码指引擎、环境以及 promise 的实施代码)

调用要求

onFulfilledonRejected必须被作为函数调用(即没有this值,在严格模式(strict)中,函数this的值为undefined;在非严格模式中其为全局对象。)

多次调用

then方法可以被同一个promise调用多次

promise成功执行时,所有onFulfilled需按照其注册顺序依次回调当promise被拒绝执行时,所有的onRejected需按照其注册顺序依次回调

返回

then方法必须返回一个promise对象

promise2 = promise1.then(onFulfilled, onRejected);复制代码

如果onFulfilled或者onRejected返回一个值x,则运行下面的Promise 解决过程:[[Resolve]](promise2, x)如果onFulfilled或者onRejected抛出一个异常e,则promise2必须拒绝执行,并返回拒因e如果onFulfilled不是函数且promise1成功执行,promise2必须成功执行并返回相同的值如果onRejected不是函数且promise1拒绝执行,promise2必须拒绝执行并返回相同的据因

不论promise1被 reject 还是被 resolve 时promise2都会被 resolve,只有出现异常时才会被 rejected。

每个Promise对象都可以在其上多次调用then方法,而每次调用then返回的Promise的状态取决于那一次调用then时传入参数的返回值,所以then不能返回this,因为then每次返回的Promise的结果都有可能不同。

接下来我们来写我们的then方法:

Promise.prototype.then = function(onResolved, onRejected) {var self = thisvar promise2// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}if (self.status === 'resolved') {// 如果promise1(此处即为this/self)的状态已经确定并且是resolved,我们调用onResolved// 因为考虑到有可能throw,所以我们将其包在try/catch块里return promise2 = new Promise(function(resolve, reject) {try {var x = onResolved(self.data)if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为promise2的结果x.then(resolve, reject)}resolve(x) // 否则,以它的返回值做为promise2的结果} catch (e) {reject(e) // 如果出错,以捕获到的错误做为promise2的结果}})}// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数,就不再做过多解释if (self.status === 'rejected') {return promise2 = new Promise(function(resolve, reject) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})}if (self.status === 'pending') {// 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected,// 只能等到Promise的状态确定后,才能确实如何处理。// 所以我们需要把我们的**两种情况**的处理逻辑做为callback放入promise1(此处即this/self)的回调数组里// 逻辑本身跟第一个if块内的几乎一致,此处不做过多解释return promise2 = new Promise(function(resolve, reject) {self.onResolvedCallback.push(function(value) {try {var x = onResolved(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})self.onRejectedCallback.push(function(reason) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})})}}// 为了下文方便,我们顺便实现一个catch方法Promise.prototype.catch = function(onRejected) {return this.then(null, onRejected)}复制代码

最后附上完整的代码:

Promise.prototype.then = function(onResolved, onRejected) {var self = thisvar promise2// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}if (self.status === 'resolved') {// 如果promise1(此处即为this/self)的状态已经确定并且是resolved,我们调用onResolved// 因为考虑到有可能throw,所以我们将其包在try/catch块里return promise2 = new Promise(function(resolve, reject) {try {var x = onResolved(self.data)if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为promise2的结果x.then(resolve, reject)}resolve(x) // 否则,以它的返回值做为promise2的结果} catch (e) {reject(e) // 如果出错,以捕获到的错误做为promise2的结果}})}// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数,就不再做过多解释if (self.status === 'rejected') {return promise2 = new Promise(function(resolve, reject) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})}if (self.status === 'pending') {// 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected,// 只能等到Promise的状态确定后,才能确实如何处理。// 所以我们需要把我们的**两种情况**的处理逻辑做为callback放入promise1(此处即this/self)的回调数组里// 逻辑本身跟第一个if块内的几乎一致,此处不做过多解释return promise2 = new Promise(function(resolve, reject) {self.onResolvedCallback.push(function(value) {try {var x = onResolved(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})self.onRejectedCallback.push(function(reason) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})})}}// 为了下文方便,我们顺便实现一个catch方法Promise.prototype.catch = function(onRejected) {return this.then(null, onRejected)}复制代码

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。