它本身是个构造函数,用来解决异步问题。本文涉及的几个前置知识:回调函数、回调地狱
回调函数
回调函数 是一个作为 变量 传递给另外一个 函数 的 函数,它在主体函数执行完之后执行。
function A 有一个参数 function B ,function B 会在 function A 执行完成之后被调用执行。
var A = function (a){
console.log(a)
}
var B =function(){
console.log('我是回调的那个函数')
}
( A(B()) ) // 我是回调的那个函数
回调地狱
回调地狱是回调函数的一个致命的弱点。假设多个请求存在依赖性。可能会写下以下代码
ajax(url, () => {
// 处理逻辑
ajax(url1, () => {
// 处理逻辑
ajax(url2, () => {
// 处理逻辑
})
})
})
以上代码看起来不利于阅读和维护,当然,你可能会想说解决这个问题还不简单,把函数分开来写不就得了
function firstAjax() {
ajax(url1, () => {
// 处理逻辑
secondAjax()
})
}
function secondAjax() {
ajax(url2, () => {
// 处理逻辑
})
}
ajax(url, () => {
// 处理逻辑
firstAjax()
})
以上的代码虽然看上去利于阅读了,但是还是没有解决根本问题。
回调地狱的根本问题就是:
嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,嵌套函数一多,就很难处理错误
当然,回调函数还存在着别的几个缺点,比如不能使用 try catch 捕获错误,不能直接 return。
所以 es6 中 promise 应运而生。
正文
promise 基本用法
Promise 翻译过来就是承诺的意思,这个承诺会在未来有一个确切的答复,并且该承诺有三种状态,分别是:
等待中(pending)
完成了 (resolved)
拒绝了(rejected)
这个承诺一旦从等待状态变成为其他状态就永远不能更改状态了
new Promise((resolve, reject) => {
resolve('success')
reject('reject') // 无效
})
当我们在构造 Promise 的时候,构造函数内部的代码是立即执行的
new Promise((resolve, reject) => {
console.log('new Promise') // new Promise
resolve('success')
})
console.log('finifsh') //finifsh
Promise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise,并且是一个全新的 Promise,原因也是因为状态不可变。如果你在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装
Promise.resolve(1)
.then(res => {
console.log(res) // => 1
return 2 // 包装成 Promise.resolve(2)
})
.then(res => {
console.log(res) // => 2
})
当然了,Promise 也很好地解决了回调地狱的问题,可以把之前的回调地狱例子改写为如下代码:
//ajax就是封装了promise的
ajax(url)
.then(res => {
console.log(res)
return ajax(url1)
}).then(res => {
console.log(res)
return ajax(url2)
}).then(res => console.log(res))
如何实现一个promise
promise 是有 简易版 和 规范版 的。
简易版 是为了理解 promise 的,基本不能用。
规范版 可以应用到实际项目中的,先写个 简易版 理解一下。
举例:
假如我们有三个网络请求,
请求2必须依赖请求1的结果
请求3必须依赖请求2的结果
const request = require("request");
// 我们先用Promise包装下三个网络请求
// 请求成功时resolve这个Promise
const request1 = function() {
const promise = new Promise((resolve) => {
request('https://www.baidu.com', function (error, response) {
if (!error && response.statusCode == 200) {
resolve('request1 success');
}
});
});
return promise;
}
const request2 = function() {
const promise = new Promise((resolve) => {
request('https://www.baidu.com', function (error, response) {
if (!error && response.statusCode == 200) {
resolve('request2 success');
}
});
});
return promise;
}
const request3 = function() {
const promise = new Promise((resolve) => {
request('https://www.baidu.com', function (error, response) {
if (!error && response.statusCode == 200) {
resolve('request3 success');
}
});
});
return promise;
}
// 先发起request1,等他resolve后再发起request2,
// 然后是request3
request1().then((data) => {
console.log(data);
return request2();
})
.then((data) => {
console.log(data);
return request3();
})
.then((data) => {
console.log(data);
})
然后写一个简易的 规范版。
首先需要知道 promise 的 标准 和 步骤
- 新建Promise需要使用new关键字,那他肯定是作为面向对象的方式调用的,Promise是一个类。关于JS的面向对象更详细的解释可以看这篇文章。
- 我们new Promise(fn)的时候需要传一个函数进去,说明Promise的参数是一个函数
- 构造函数传进去的fn会收到resolve和reject两个函数,用来表示Promise成功和失败,说明构造函数里面还需要resolve和reject这两个函数,这两个函数的作用是改变Promise的状态。
- 根据规范,promise有pending,fulfilled,rejected三个状态,初始状态为pending,调用resolve会将其改为fulfilled,调用reject会改为rejected。
- promise实例对象建好后可以调用then方法,而且是可以链式调用then方法,说明then是一个实例方法。链式调用的实现这篇有详细解释,我这里不再赘述。简单的说就是then方法也必须返回一个带then方法的对象,可以是this或者新的promise实例。 ```javascript // 先定义三个常量表示状态 const PENDING = ‘pending’; const FULFILLED = ‘fulfilled’; const REJECTED = ‘rejected’;
//根据规范,resolve方法是将状态改为fulfilled,reject是将状态改为rejected。 // 这两个方法直接写在构造函数里面 function MyPromise(fn) { this.status = PENDING; // 初始状态为pending this.value = null; // 初始化value this.reason = null; // 初始化reason
// 构造函数里面添加两个数组存储成功和失败的回调 this.onFulfilledCallbacks = []; this.onRejectedCallbacks = [];
// 存一下this,以便resolve和reject里面访问 var that = this; // resolve方法参数是value function resolve(value) { if (that.status === PENDING) { that.status = FULFILLED; that.value = value;
// resolve里面将所有成功的回调拿出来执行
that.onFulfilledCallbacks.forEach(callback => {
callback(that.value);
});
}
}
// reject方法参数是reason function reject(reason) { if (that.status === PENDING) { that.status = REJECTED; that.reason = reason;
// resolve里面将所有失败的回调拿出来执行
that.onRejectedCallbacks.forEach(callback => {
callback(that.reason);
});
}
}
try { fn(resolve, reject); } catch (error) { reject(error); } }
function resolvePromise(promise, x, resolve, reject) { // 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise // 这是为了防止死循环 if (promise === x) { return reject(new TypeError(‘The promise and the return value are the same’)); }
if (x instanceof MyPromise) { // 如果 x 为 Promise ,则使 promise 接受 x 的状态 // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y // 这个if跟下面判断then然后拿到执行其实重复了,可有可无 x.then(function (y) { resolvePromise(promise, y, resolve, reject); }, reject); } // 如果 x 为对象或者函数 else if (typeof x === ‘object’ || typeof x === ‘function’) { // 这个坑是跑测试的时候发现的,如果x是null,应该直接resolve if (x === null) { return resolve(x); }
try {
// 把 x.then 赋值给 then
var then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (typeof then === 'function') {
var called = false;
// 将 x 作为函数的作用域 this 调用之
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
// 名字重名了,我直接用匿名函数了
try {
then.call(
x,
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
function (y) {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
// 实现这条需要前面加一个变量called
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
function (r) {
if (called) return;
called = true;
reject(r);
});
} catch (error) {
// 如果调用 then 方法抛出了异常 e:
// 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
if (called) return;
// 否则以 e 为据因拒绝 promise
reject(error);
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} else { // 如果 x 不为对象或者函数,以 x 为参数执行 promise resolve(x); } }
MyPromise.prototype.then = function (onFulfilled, onRejected) { // 如果onFulfilled不是函数,给一个默认函数,返回value // 后面返回新promise的时候也做了onFulfilled的参数检查,这里可以删除,暂时保留是为了跟规范一一对应,看得更直观 var realOnFulfilled = onFulfilled; if (typeof realOnFulfilled !== ‘function’) { realOnFulfilled = function (value) { return value; } }
// 如果onRejected不是函数,给一个默认函数,返回reason的Error // 后面返回新promise的时候也做了onRejected的参数检查,这里可以删除,暂时保留是为了跟规范一一对应,看得更直观 var realOnRejected = onRejected; if (typeof realOnRejected !== ‘function’) { realOnRejected = function (reason) { throw reason; } }
var that = this; // 保存一下this
if (this.status === FULFILLED) { var promise2 = new MyPromise(function (resolve, reject) { setTimeout(function () { try { if (typeof onFulfilled !== ‘function’) { resolve(that.value); } else { var x = realOnFulfilled(that.value); resolvePromise(promise2, x, resolve, reject); } } catch (error) { reject(error); } }, 0); });
return promise2;
}
if (this.status === REJECTED) { var promise2 = new MyPromise(function (resolve, reject) { setTimeout(function () { try { if (typeof onRejected !== ‘function’) { reject(that.reason); } else { var x = realOnRejected(that.reason); resolvePromise(promise2, x, resolve, reject); } } catch (error) { reject(error); } }, 0); });
return promise2;
}
// 如果还是PENDING状态,将回调保存下来 if (this.status === PENDING) { var promise2 = new MyPromise(function (resolve, reject) { that.onFulfilledCallbacks.push(function () { setTimeout(function () { try { if (typeof onFulfilled !== ‘function’) { resolve(that.value); } else { var x = realOnFulfilled(that.value); resolvePromise(promise2, x, resolve, reject); } } catch (error) { reject(error); } }, 0); }); that.onRejectedCallbacks.push(function () { setTimeout(function () { try { if (typeof onRejected !== ‘function’) { reject(that.reason); } else { var x = realOnRejected(that.reason); resolvePromise(promise2, x, resolve, reject); } } catch (error) { reject(error); } }, 0) }); });
return promise2;
} }
MyPromise.deferred = function () { var result = {}; result.promise = new MyPromise(function (resolve, reject) { result.resolve = resolve; result.reject = reject; });
return result; }
MyPromise.resolve = function (parameter) { if (parameter instanceof MyPromise) { return parameter; }
return new MyPromise(function (resolve) { resolve(parameter); }); }
MyPromise.reject = function (reason) { return new MyPromise(function (resolve, reject) { reject(reason); }); }
MyPromise.all = function (promiseList) { var resPromise = new MyPromise(function (resolve, reject) { var count = 0; var result = []; var length = promiseList.length;
if (length === 0) {
return resolve(result);
}
promiseList.forEach(function (promise, index) {
MyPromise.resolve(promise).then(function (value) {
count++;
result[index] = value;
if (count === length) {
resolve(result);
}
}, function (reason) {
reject(reason);
});
});
});
return resPromise; }
MyPromise.race = function (promiseList) { var resPromise = new MyPromise(function (resolve, reject) { var length = promiseList.length;
if (length === 0) {
return resolve();
} else {
for (var i = 0; i < length; i++) {
MyPromise.resolve(promiseList[i]).then(function (value) {
return resolve(value);
}, function (reason) {
return reject(reason);
});
}
}
});
return resPromise; }
MyPromise.prototype.catch = function (onRejected) { this.then(null, onRejected); }
MyPromise.prototype.finally = function (fn) { return this.then(function (value) { return MyPromise.resolve(fn()).then(function () { return value; }); }, function (error) { return MyPromise.resolve(fn()).then(function () { throw error }); }); }
MyPromise.allSettled = function (promiseList) { return new MyPromise(function (resolve) { var length = promiseList.length; var result = []; var count = 0;
if (length === 0) {
return resolve(result);
} else {
for (var i = 0; i < length; i++) {
(function (i) {
var currentPromise = MyPromise.resolve(promiseList[i]);
currentPromise.then(function (value) {
count++;
result[i] = {
status: 'fulfilled',
value: value
}
if (count === length) {
return resolve(result);
}
}, function (reason) {
count++;
result[i] = {
status: 'rejected',
reason: reason
}
if (count === length) {
return resolve(result);
}
});
})(i)
}
}
}); }
module.exports = MyPromise; ```