它本身是个构造函数,用来解决异步问题。本文涉及的几个前置知识:回调函数、回调地狱
回调函数
回调函数 是一个作为 变量 传递给另外一个 函数 的 函数,它在主体函数执行完之后执行。
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 Promiseresolve('success')})console.log('finifsh') //finifsh
Promise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise,并且是一个全新的 Promise,原因也是因为状态不可变。如果你在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装
Promise.resolve(1).then(res => {console.log(res) // => 1return 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这个Promiseconst 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,// 然后是request3request1().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 赋值给 thenvar then = x.then;} catch (error) {// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promisereturn 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 均被调用,// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用// 实现这条需要前面加一个变量calledif (called) return;called = true;resolvePromise(promise, y, resolve, reject);},// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promisefunction (r) {if (called) return;called = true;reject(r);});} catch (error) {// 如果调用 then 方法抛出了异常 e:// 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之if (called) return;// 否则以 e 为据因拒绝 promisereject(error);}} else {// 如果 then 不是函数,以 x 为参数执行 promiseresolve(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; ```
