它本身是个构造函数,用来解决异步问题。本文涉及的几个前置知识:回调函数、回调地狱

回调函数

回调函数 是一个作为 变量 传递给另外一个 函数 的 函数,它在主体函数执行完之后执行。
function A 有一个参数 function B ,function B 会在 function A 执行完成之后被调用执行。

  1. var A = function (a){
  2. console.log(a)
  3. }
  4. var B =function(){
  5. console.log('我是回调的那个函数')
  6. }
  7. ( A(B()) ) // 我是回调的那个函数

回调地狱

回调地狱是回调函数的一个致命的弱点。假设多个请求存在依赖性。可能会写下以下代码

  1. ajax(url, () => {
  2. // 处理逻辑
  3. ajax(url1, () => {
  4. // 处理逻辑
  5. ajax(url2, () => {
  6. // 处理逻辑
  7. })
  8. })
  9. })

以上代码看起来不利于阅读和维护,当然,你可能会想说解决这个问题还不简单,把函数分开来写不就得了

  1. function firstAjax() {
  2. ajax(url1, () => {
  3. // 处理逻辑
  4. secondAjax()
  5. })
  6. }
  7. function secondAjax() {
  8. ajax(url2, () => {
  9. // 处理逻辑
  10. })
  11. }
  12. ajax(url, () => {
  13. // 处理逻辑
  14. firstAjax()
  15. })

以上的代码虽然看上去利于阅读了,但是还是没有解决根本问题。
回调地狱的根本问题就是:
嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,嵌套函数一多,就很难处理错误
当然,回调函数还存在着别的几个缺点,比如不能使用 try catch 捕获错误,不能直接 return。
所以 es6 中 promise 应运而生。

正文

promise 基本用法

Promise 翻译过来就是承诺的意思,这个承诺会在未来有一个确切的答复,并且该承诺有三种状态,分别是:
等待中(pending)
完成了 (resolved)
拒绝了(rejected)
这个承诺一旦从等待状态变成为其他状态就永远不能更改状态了

  1. new Promise((resolve, reject) => {
  2. resolve('success')
  3. reject('reject') // 无效
  4. })

当我们在构造 Promise 的时候,构造函数内部的代码是立即执行的

  1. new Promise((resolve, reject) => {
  2. console.log('new Promise') // new Promise
  3. resolve('success')
  4. })
  5. console.log('finifsh') //finifsh

Promise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise,并且是一个全新的 Promise,原因也是因为状态不可变。如果你在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装

  1. Promise.resolve(1)
  2. .then(res => {
  3. console.log(res) // => 1
  4. return 2 // 包装成 Promise.resolve(2)
  5. })
  6. .then(res => {
  7. console.log(res) // => 2
  8. })

当然了,Promise 也很好地解决了回调地狱的问题,可以把之前的回调地狱例子改写为如下代码:

  1. //ajax就是封装了promise的
  2. ajax(url)
  3. .then(res => {
  4. console.log(res)
  5. return ajax(url1)
  6. }).then(res => {
  7. console.log(res)
  8. return ajax(url2)
  9. }).then(res => console.log(res))

如何实现一个promise

promise 是有 简易版 规范版 的。
简易版 是为了理解 promise 的,基本不能用。
规范版 可以应用到实际项目中的,先写个 简易版 理解一下。
举例:
假如我们有三个网络请求,
请求2必须依赖请求1的结果
请求3必须依赖请求2的结果

  1. const request = require("request");
  2. // 我们先用Promise包装下三个网络请求
  3. // 请求成功时resolve这个Promise
  4. const request1 = function() {
  5. const promise = new Promise((resolve) => {
  6. request('https://www.baidu.com', function (error, response) {
  7. if (!error && response.statusCode == 200) {
  8. resolve('request1 success');
  9. }
  10. });
  11. });
  12. return promise;
  13. }
  14. const request2 = function() {
  15. const promise = new Promise((resolve) => {
  16. request('https://www.baidu.com', function (error, response) {
  17. if (!error && response.statusCode == 200) {
  18. resolve('request2 success');
  19. }
  20. });
  21. });
  22. return promise;
  23. }
  24. const request3 = function() {
  25. const promise = new Promise((resolve) => {
  26. request('https://www.baidu.com', function (error, response) {
  27. if (!error && response.statusCode == 200) {
  28. resolve('request3 success');
  29. }
  30. });
  31. });
  32. return promise;
  33. }
  34. // 先发起request1,等他resolve后再发起request2,
  35. // 然后是request3
  36. request1().then((data) => {
  37. console.log(data);
  38. return request2();
  39. })
  40. .then((data) => {
  41. console.log(data);
  42. return request3();
  43. })
  44. .then((data) => {
  45. console.log(data);
  46. })

然后写一个简易的 规范版。
首先需要知道 promise 的 标准 和 步骤

  1. 新建Promise需要使用new关键字,那他肯定是作为面向对象的方式调用的,Promise是一个类。关于JS的面向对象更详细的解释可以看这篇文章。
  2. 我们new Promise(fn)的时候需要传一个函数进去,说明Promise的参数是一个函数
  3. 构造函数传进去的fn会收到resolve和reject两个函数,用来表示Promise成功和失败,说明构造函数里面还需要resolve和reject这两个函数,这两个函数的作用是改变Promise的状态。
  4. 根据规范,promise有pending,fulfilled,rejected三个状态,初始状态为pending,调用resolve会将其改为fulfilled,调用reject会改为rejected。
  5. 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;

  1. // resolve里面将所有成功的回调拿出来执行
  2. that.onFulfilledCallbacks.forEach(callback => {
  3. callback(that.value);
  4. });
  5. }

}

// reject方法参数是reason function reject(reason) { if (that.status === PENDING) { that.status = REJECTED; that.reason = reason;

  1. // resolve里面将所有失败的回调拿出来执行
  2. that.onRejectedCallbacks.forEach(callback => {
  3. callback(that.reason);
  4. });
  5. }

}

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); }

  1. try {
  2. // 把 x.then 赋值给 then
  3. var then = x.then;
  4. } catch (error) {
  5. // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
  6. return reject(error);
  7. }
  8. // 如果 then 是函数
  9. if (typeof then === 'function') {
  10. var called = false;
  11. // 将 x 作为函数的作用域 this 调用之
  12. // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
  13. // 名字重名了,我直接用匿名函数了
  14. try {
  15. then.call(
  16. x,
  17. // 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
  18. function (y) {
  19. // 如果 resolvePromise 和 rejectPromise 均被调用,
  20. // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
  21. // 实现这条需要前面加一个变量called
  22. if (called) return;
  23. called = true;
  24. resolvePromise(promise, y, resolve, reject);
  25. },
  26. // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
  27. function (r) {
  28. if (called) return;
  29. called = true;
  30. reject(r);
  31. });
  32. } catch (error) {
  33. // 如果调用 then 方法抛出了异常 e:
  34. // 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
  35. if (called) return;
  36. // 否则以 e 为据因拒绝 promise
  37. reject(error);
  38. }
  39. } else {
  40. // 如果 then 不是函数,以 x 为参数执行 promise
  41. resolve(x);
  42. }

} 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); });

  1. 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); });

  1. 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) }); });

  1. 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;

  1. if (length === 0) {
  2. return resolve(result);
  3. }
  4. promiseList.forEach(function (promise, index) {
  5. MyPromise.resolve(promise).then(function (value) {
  6. count++;
  7. result[index] = value;
  8. if (count === length) {
  9. resolve(result);
  10. }
  11. }, function (reason) {
  12. reject(reason);
  13. });
  14. });

});

return resPromise; }

MyPromise.race = function (promiseList) { var resPromise = new MyPromise(function (resolve, reject) { var length = promiseList.length;

  1. if (length === 0) {
  2. return resolve();
  3. } else {
  4. for (var i = 0; i < length; i++) {
  5. MyPromise.resolve(promiseList[i]).then(function (value) {
  6. return resolve(value);
  7. }, function (reason) {
  8. return reject(reason);
  9. });
  10. }
  11. }

});

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;

  1. if (length === 0) {
  2. return resolve(result);
  3. } else {
  4. for (var i = 0; i < length; i++) {
  5. (function (i) {
  6. var currentPromise = MyPromise.resolve(promiseList[i]);
  7. currentPromise.then(function (value) {
  8. count++;
  9. result[i] = {
  10. status: 'fulfilled',
  11. value: value
  12. }
  13. if (count === length) {
  14. return resolve(result);
  15. }
  16. }, function (reason) {
  17. count++;
  18. result[i] = {
  19. status: 'rejected',
  20. reason: reason
  21. }
  22. if (count === length) {
  23. return resolve(result);
  24. }
  25. });
  26. })(i)
  27. }
  28. }

}); }

module.exports = MyPromise; ```