摘要:如何写一个符合规范的实现前言是异步编程的一种解决方案从语法上讲,是一个对象,从它可以获取异步操作的消息从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
如何写一个符合promiseA+规范的promise实现 前言
Promise 是异步编程的一种解决方案:编写符合promiseA+规范的promise实现
从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
在实现之前,可以先看一下Promise A plus规范
1. 创建promise构造函数这里先实现promise最基本的功能:promise创建后立即执行;在then时执行相应的函数;捕获错误立即变成reject态。
// promise里只有一个参数,叫executor(执行器) function Promise(executor) { let self = this; self.status = "pending";//等待态 self.value = undefined;//默认成功的值 self.err = undefined;//默认失败的值 function resolve(value) { if (self.status === "pending") { self.status = "resolved"; self.value = value; } } function reject(err) { if (self.status === "pending") { self.status = "rejected"; self.err = err; } } try {//捕获时发生异常,直接变成reject态,抛出错误 executor(resolve, reject);//promise实例创建后,立即执行 } catch (error) { reject(error); } } //在prototype上定义then实例方法 Promise.prototype.then = function (onFulfilled, onRejected) { let self = this; if (self.status === "resolved") { onFulfilled(self.value); } if (self.status === "rejected") { onRejected(self.err); } }
这里我们先测试一下我们的Promise
这里便实现了基本功能,前面说过Promise 是异步编程的一种解决方案;
我们加个异步逻辑运行一下:
我们都知道异步代码并不会立即执行,这时既不是resolved也不是rejected,而是pending。
在之前的状态判断里面,正好丢了一个pending状态。OK,这时需要在then里判断当status为pending时,先将onFulfilled, onRejected存入数组里,当status改变时,再遍历数组让里面的函数依次执行,看代码。
(1)申明两个存放onFulfiled,onRejected的数组
function Promise(resolver) { let self = this; self.status = "pending";//等待态 self.value = undefined;//默认成功的值 self.err = undefined;//默认失败的值 self.onResolvedCallbacks = []; // 存放then成功的回调 self.onRejectedCallbacks = []; // 存放then失败的回调 function resolve(value) { if (self.status === "pending") { self.status = "resolved"; self.value = value; self.onResolvedCallbacks.forEach(fn=>{//调用resolve时,依次执行数组里的函数 fn(); }) } } function reject(err) { if (self.status === "pending") { self.status = "rejected"; self.err = err; self.onRejectedCallbacks.forEach(fn=>{ fn(); }) } } try {//捕获时发生异常,直接抛出错误 resolver(resolve, reject);//promise实例创建后,立即执行它的方法 } catch (error) { reject(error) } }
(2)接着在then方法里添加pending的判断
Promise.prototype.then = function (onFulfilled, onRejected) { let self = this; if (self.status === "resolved") { onFulfilled(self.value); } if (self.status === "rejected") { onRejected(self.err); } if(self.status==="pending"){// 此时没resolved,也没rejectd self.onResolvedCallbacks.push(()=>{ onFulfilled(self.value); }); self.onRejectedCallbacks.push(()=>{ onRejected(self.err); }) } }
再看刚刚的异步逻辑
1s后就执行成功了,是不是很神奇,再看下面:
3. Promise链式调用(1)规范里说在同一个promise里then可以被多次调用。
(2)jquery能实现链式调用靠的是返回this,而promise不能返回this,规范里又说它返回的是一个新的Promise实例(注意,不是原来那个Promise实例);
在then里新建一个promise2并为每一个状态包一个Promise
写到这里,再来看看规范,规范里说道
(1)x可能是一个promise;
(2)可能是一个对象或者方法;
(3)也有可能是一个普通的值。
这时需要一个方法来处理x
3.1 对onFulfilled和onRejected的返回值进行处理于是引入一个处理方法resolvePromise(promise2, x, resolve, reject);
这里需要注意一下,有些人写的promise可能会既调用成功,又调用失败,如果两个都调用先调用谁另一个就忽略掉。
于是增加一个判断called表示是否调用过成功或者失败,看代码:
function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) {//promise2和x不能相同 return reject(new TypeError("循环引用了")) } let called;// 表示是否调用过成功或者失败 //这里对x的类型进行判断 if (x !== null && (typeof x === "object" || typeof x === "function")) { try { // 判断x是不是promise,如果x是对象并且x的then方法是函数我们就认为他是一个promise let then = x.then; if (typeof then === "function") { then.call(x, function (y) { if (called) return called = true // y可能还是一个promise,在去解析直到返回的是一个普通值 resolvePromise(promise2, y, resolve, reject) }, function (err) { //失败 if (called) return called = true reject(err); }) } else { resolve(x) } } catch (e) { if (called) return called = true; reject(e); } } else { // 说明是一个普通值1 resolve(x); // 表示成功了 } }
相应的将前面的代码进行一些更改
如果在then中什么都不传,值会穿透到最后调用的时候;
这时需要在then里给onFulfilled和onRejected写一个默认的函数
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : function (value) { return value; } onRejected = typeof onRejected === "function" ? onRejected : function (err) { throw err;//这里需要抛出错误,不能return err,否则会在下一次调用成功态 }5. then的异步实现
规范里要求,所有的onFulfilled和onRejected都要确保异步执行
这里以resolve为例,写一个setTimeout():
在使用promise的过程中,我们都需要先new Promise(),比如说:
function read() { let fs = require("fs"); let promise = new Promise(function(resolve,reject){ fs.readFile("./1.txt","utf8",function(err,data){ if(err) reject(err); resolve(data); }) }); return promise }
在Promise中,它为我们提供了一个语法糖Promise.defer,用Promise.defer只需这样写:
function read() { let defer = Promise.defer() require("fs").readFile(".//1.txt", "utf8", function (err, data) { if(err) defer.reject(err); defer.resolve(data); }) return defer.promise; }
为此,再为我们的Promise加一个defer方法:
Promise.defer = Promise.deferred = function () { let dfd = {}; dfd.promise = new Promise(function (resolve, reject) { dfd.resolve = resolve; dfd.reject = reject; }); return dfd }
在这里,我们基本实现了一个比较完整的promise;当然Promise还有许多静态方法,还有js的异步发展史,这些可以在下一次进行讨论。
完整代码:
// promise里只有一个参数,叫executor(执行器) function Promise(executor) { let self = this; self.status = "pending";//等待态 self.value = undefined;//默认成功的值 self.err = undefined;//默认失败的值 self.onResolvedCallbacks = []; // 存放then成功的回调 self.onRejectedCallbacks = []; // 存放then失败的回调 function resolve(value) { if (self.status === "pending") { self.status = "resolved"; self.value = value; self.onResolvedCallbacks.forEach(function (fn) { fn(); }); } } function reject(err) { if (self.status === "pending") { self.status = "rejected"; self.err = err; self.onRejectedCallbacks.forEach(function (fn) { fn(); }); } } try {//捕获时发生异常,直接变成reject态,抛出错误 executor(resolve, reject);//promise实例创建后,立即执行 } catch (error) { reject(error); } } function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) {//promise2和x不能相同 return reject(new TypeError("循环引用了")) } let called;// 表示是否调用过成功或者失败 //这里对x的类型进行判断 if (x !== null && (typeof x === "object" || typeof x === "function")) { try { // 判断x是不是promise,如果x是对象并且x的then方法是函数我们就认为他是一个promise let then = x.then; if (typeof then === "function") { then.call(x, function (y) { if (called) return called = true // y可能还是一个promise,在去解析直到返回的是一个普通值 resolvePromise(promise2, y, resolve, reject) }, function (err) { //失败 if (called) return called = true reject(err); }) } else { resolve(x) } } catch (e) { if (called) return called = true; reject(e); } } else { // 说明是一个普通值1 resolve(x); // 表示成功了 } } //在prototype上定义then实例方法 Promise.prototype.then = function (onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === "function" ? onFulfilled : function (value) { return value; } onRejected = typeof onRejected === "function" ? onRejected : function (err) { throw err;//这里需要抛出错误,不能return err,否则会在下一次调用成功态 } let self = this; let promise2; //返回的promise if (self.status === "resolved") { promise2 = new Promise(function (resolve, reject) { setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }) } if (self.status === "rejected") { promise2 = new Promise(function (resolve, reject) { setTimeout(function () { try { let x = onRejected(self.err); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }) } // 当调用then时可能没成功 也没失败 if (self.status === "pending") { promise2 = new Promise(function (resolve, reject) { // 此时没有resolve 也没有reject self.onResolvedCallbacks.push(function () { setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }) }); self.onRejectedCallbacks.push(function () { setTimeout(function () { try { let x = onRejected(self.err); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }); }) } return promise2; } Promise.defer = Promise.deferred = function () { let dfd = {}; dfd.promise = new Promise(function (resolve, reject) { dfd.resolve = resolve; dfd.reject = reject; }); return dfd } module.exports = Promise;7.Promise测试
npm i -g promises-aplus-tests promises-aplus-tests Promise.js
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/107622.html
摘要:以上代码,可以完美通过所有用例。在的函数中,为何需要这个同样是因为规范中明确表示因此我们需要这样的来确保只会执行一次。其他情况,直接返回以该值为成功状态的对象。 Promise是前端面试中的高频问题,我作为面试官的时候,问Promise的概率超过90%,据我所知,大多数公司,都会问一些关于Promise的问题。如果你能根据PromiseA+的规范,写出符合规范的源码,那么我想,对于面试...
摘要:今天我们来自己手写一个符合规范的库。是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大。我们可以看到,其实就是一个构造函数。所以说我们的数组里存的是一个一个的的回调函数,也就是一个一个。 今天我们来自己手写一个符合PromiseA+规范的Promise库。大家是不是很激动呢?? showImg(https://segmentfault.com/img/bV6t4Z?...
摘要:我们都知道,方法中有和两个回调函数,所以我们要处理一下这两个回调函数。我们实现了异步调用,在方法中返回或者值,实现了方法中可以没有回调函数也能把执行结果传入下一次的方法中。 Hello everybody。我又来啦,还记得我们上一张实现的内容吗? showImg(https://segmentfault.com/img/bV6UaU?w=102&h=95); 上一张我们实现了一个简单的...
摘要:不同的的实现需要可以相互调用,搞清楚了标准之后,开始动手吧构造函数产生一个对象有很多种方法,构造函数是看起来最面向对象的一种,而且原生实现也是使用的构造函数,因此我也决定使用构造函数的方法。 -- What i cant create, i dont understant 前言 实现Promise的目的是为了深入的理解Promies,以在项目中游刃有余的使用它。完整的代码见gitHub...
摘要:嗝首先,我们通过字面可以看出来是一种解决方案,而且还有两种传统的解决方案回调函数和事件,,那么我们就来先聊聊这两种方案。 前言 虽然今年已经18年,但是今天还是要继续聊聊ES6的东西,ES6已经过去几年,可是我们对于ES6的语法究竟是掌握了什么程度,是了解?会用?还是精通?相信大家和我一样都对自己有着一个提升的心,对于新玩具可不能仅仅了解,对于其中的思想才是最吸引人的,所以接下来会通过...
阅读 3637·2021-11-19 09:40
阅读 3095·2019-08-30 15:54
阅读 2311·2019-08-30 15:44
阅读 3194·2019-08-29 15:35
阅读 3331·2019-08-29 12:22
阅读 2860·2019-08-28 18:01
阅读 3140·2019-08-26 13:54
阅读 901·2019-08-26 12:24