前言
Promise 是异步编程的一种解决方案: 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。 promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
编写符合promiseA+规范的promise实现
在实现之前,可以先看一下
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 是异步编程的一种解决方案; 我们加个异步逻辑运行一下:
2. 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
这里就返回了一个新的promise2,在promise2里也需要调用resolve或者reject;这里申明一个 x 来储存上一次then的返回值 规范里说只要上一次then有返回值,下一次then一定调用成功态resolve(x) 再来看看规范,规范里说道
(1)x可能是一个promise;
(2)可能是一个对象或者方法;
(3)也有可能是一个普通的值。
这时需要一个方法来处理x
3.1 对onFulfilled和onRejected的返回值进行处理
于是引入一个处理方法resolvePromise(promise2, x, resolve, reject); 这里四个参数分别是
- Promise2是我们返回的新的promise
- x是上一次then的返回值
- resolve是成功的方法
- reject是失败的方法
这里需要注意一下,有些人写的promise可能会既调用成功,又调用失败,如果两个都调用先调用谁另一个就忽略掉。 在resolvePromise(promise2, x, resolve, reject)里增加一个判断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); // 表示成功了 }}复制代码
相应的将前面的代码进行一些更改
这时我们就实现了promise的链式调用。4. 值的穿透问题
如果在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():
6. defer语法糖
在使用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-testspromises-aplus-tests Promise.js复制代码