前言
Promise 是异步编程的一种解决方案: 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。 promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
编写符合promiseA+规范的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 是异步编程的一种解决方案; 我们加个异步逻辑运行一下:
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); // 表示成功了
}
}
复制代码
相应的将前面的代码进行一些更改
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-tests
promises-aplus-tests Promise.js
复制代码