Promise的用法、原理、手写实现
1. promise 入门简介
Promise 是用来解决异步编程的问题
1.1 JS 中分同步Api 和 异步Api。
同步API:只有当前API执行完成后,才能继续执行下一个API
for (let i = 0; i < 10000; i++) { console.log(i); } console.log('同步代码执行');
只有上面一万行数值打印完,才会打印’同步代码执行’
异步API:当前API的执行不会阻塞后续代码的执行
console.log('before'); setTimeout( () => { console.log('last'); }, 2000); console.log('after');
setTimeout定时器要在2s秒后才执行,js引擎不会卡在定时器这,会先执行同步代码,等同步代码执行完再执行异步代码定时器(在这只需要先记住定时器是异步代码)
1.2 同步API, 异步API的区别( 获取返回值 )
同步API可以从返回值中拿到API执行的结果, 但是异步API是不可以的
同步:
// 同步 function sum (n1, n2) { return n1 + n2; } const result = sum(10, 20); // result 值为 30
异步:
function getMsg () { setTimeout(function () { return { msg: 'Hello Node.js' } }, 2000); } const msg = getMsg(); // msg 的值是 undefined
所以异步函数没法用返回值获取值
1.3 回调函数
自己定义函数让别人去调用。
使用回调函数可以获取异步API执行结果
function getMsg (callback) { setTimeout(() => { let a = '异步函数结果' callback(a) }, 2000) } getMsg((result) => { console.log(result); // 异步函数结果 })
1.4代码执行顺序分析
console.log('代码开始执行'); setTimeout(() => { console.log('2秒后执行的代码'); }, 2000); setTimeout(() => { console.log('0秒后执行的代码'); }, 0) console.log('代码结束执行')
异步代码执行区的异步函数执行完成,将要执行专属的回调函数时,就会将回调函数放入回调函数队列,等同步代码执行区的代码执行完成后,就把回调函数队列的回调函数加入同步代码执行区。
1.5 JS 常见异步编程
- fs 文件操作
- 数据库操作
- AJAX 网络请求
- 定时器 (setTimeout)
1.6 Promise 出现的需求
我们先来个场景:
现在需要用 fs 文件操作 读取文件,但读取文件的顺序必须是先读A,再读B,再读C。
因为 fs 文件操作是异步的,没办法写成同步代码那样,按顺序如下
假设 fs 文件操作 是 同步的
const fs = require('fs') fs.readFile('a.txt'); fs.readFile('b.txt'); fs.readFile('c.txt');
但 fs文件操作 是异步编程, 要按照顺序读取的话就不能写成同步代码的形式,只能这样写:
const fs = require('fs') fs.readFile('a.txt', (err, data) => { console.log('第一个执行', data); fs.readFile('b.txt', (err, data) => { console.log('第二个执行', data); fs.readFile('c.txt', (err, data) => { console.log(data); }) }) })
连续嵌套着的回调函数可读性非常差,也称为回调地狱
Promise出现的目的是解决Node.js异步编程中回调地狱的问题。
先来一个Promise 案例,有个感性的认识
// resolve 解决 // reject 拒绝 // promise 的执行流程如下: // promise 接收的参数是一个回调函数,回调函数有两个参数,resolve和 reject,在这个回调函数内部包裹一个异步操作,这个异步操作成功就调用resolve函数,失败就调用 reject // promise 可以进行链式调用,promise.then() 是对象成功的回调, promise.catch() 是对象失败的回调 let promise = new Promise((resolve, reject) => { setTimeout(() => { if (true) { resolve({name: 'aaaaa'}) // 将 promise 对象的状态设置为 成功 } else { reject('失败了') // 将 promise 对象的状态设置为 失败 } }, 2000); }); promise.then(result => { console.log(result); }).catch( err => { console.log(err); })
先展示 promise 解决回调地狱的问题,后面会详细介绍promise。
Promise解决 按顺序读取A文件,B文件,C文件 的回调地狱问题:
先有个感性的认识
const fs = require('fs') let p1 = new Promise((resolve, reject) => { fs.readFile('a.txt', 'utf-8', (err, data) => { resolve(data) }) }) let p2 = new Promise((resolve, reject) => { fs.readFile('b.txt', 'utf-8', (err, data) => { resolve(data) }) }) let p3 = new Promise((resolve, reject) => { fs.readFile('c.txt', 'utf-8', (err, data) => { resolve(data) }) }) p1.then(r1 => { console.log(r1); return p2; }).then(r2 => { console.log(r2); return p3; }).then(r3 => { console.log(r3); })
2. Promise 常见常见练习,对Promise 有更好的熟悉感
2.1 promise 实践练习-fs读取文件
// 不使用 promise const fs = require('fs'); fs.readFile('./test.txt', (err, data) => { // 出错,抛出错误 if (err) throw err; console.log(data.toString()); }) // 使用 Promise 包裹 let promise = new Promise((resolve, reject) => { fs.readFile('./test.txt', (err, data) => { if (err) reject(err); resolve(data.toString()); }) }) // 调用 promise 封装的异步函数 promise.then(result => { console.log(result); })
2.2 promise 实践练习-AJAX请求
// 原生 const btn = document.querySelector('#btn'); btn.addEventListener("click", () => { // 创建对象 const xhr = new XMLHttpRequest(); // 初始化 xhr.open('GET', 'https://api.apiopen.top/getJoke'); // 发送 xhr.send(); // 处理响应结果 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { // 判断响应状态码 if (xhr.status >= 200 && xhr.status < 300) { // 控制台输出响应体 console.log(xhr.response); } else { // 控制台输出状态码 console.log(xhr.status); } } } }) // promise 封装 btn.addEventListener("click", () => { // 创建 Promise const promise = new Promise((resolve, reject) => { // 创建对象 const xhr = new XMLHttpRequest(); // 初始化 xhr.open('GET', 'https://api.apiopen.top/getJoke'); // 发送 xhr.send(); // 处理响应结果 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { // 判断响应状态码 if (xhr.status >= 200 && xhr.status < 300) { // 控制台输出响应体 resolve(xhr.response); } else { // 控制台输出状态码 reject(xhr.status); } } } }) promise.then(result => { console.log(result); }).catch(err => { console.log(err); }) })
2.3 util.promisify 方法进行 promise 风格转化
这个是用在node.js 的环境下,我在用node.js 写后端时用到过这种方法
const util = require('util'); const fs = require('fs'); // 返回一个新的函数 // 这个函数的返回结果是promise 对象 let mineReadFile = util.promisify(fs.readFile); mineReadFile('./test.txt').then(result => { console.log(result); }).catch(err => { console.log(err); })
2.4 promise 封装练习-AJAX请求
/** * 封装一个函数 sendAJAX 发送 GET AJAX 请求 * 参数 URL * 返回结果 Promise 对象 **/ function sendAJAX(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.send(); // 处理结果 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response); } else { reject(xhr.status) } } } }) } sendAJAX('https://api.apiopen.top/getJoke').then(result => { console.log(result); }).catch(err => { console.log(err); })
3. promise 的详细介绍
在了解 promise 的基本流程前,先要知道 promise的一些基本属性
3.1 promise 的状态
promise 的状态时 promise实例对象中的一个属性 [PromiseState]
pending 进行中
resolved / fulfilled 成功
rejected 失败
状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态。
3.2. promise 对象的值
实例对象中的另一个属性 [PromiseResult]
保存着异步任务 [成功/失败] 的结果
- resolve
- reject
3.3 promise 的基本流程
3.4 promise Api 的详细介绍
3.4.1 Promise 构造函数:Promise(executor)
1) executor 函数:执行器 (resolve, reject) => {}
(2) resolve 函数:内部定义成功时我们调用的函数 value => {}
(3) reject 函数:内部定义失败时我们调用的函数 reason => {}
说明:executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行代码
<script> let p = new Promise((resolve, reject) => { // 同步调用 console.log(111); }); console.log(222); </script>
3.4.2 Promise.prototype.then 方法 (onResolved, onRejected) => {}
(1) onResolved 函数:成功的回调函数 (value) => {}
(2) onRejected 函数:失败的回调函数 (reason) => {}说明:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调返回一个新的 promise 对象
promise.then(value => { console.log(value); }, reason => { console.log(reason); })
3.4.3 Promise.catch 方法:(reason) => {}
(1) reason:失败的数据或Promise对象
说明:返回一个 失败的 promise 对象
sendAJAX('https://api.apiopen.top/getJoke').catch(reason => { console.log(reason); })
3.4.4 Promise.resolve 方法:(value) => {}
(1) value:成功的数据或 promise 对象
说明:返回一个成功/失败的 promise 对象
// 如果传入的参数为 非 promise类型的对象,则返回的结果为成功的promise对象 let p1 = Promise.resolve(521); // 如果传入的参数为 promise 对象,则参数的结果决定了 resolve 的结果 let p2 = Promise.resolve(new Promise((resolve, reject) => { resolve('ok'); })) console.log(p2);
3.4.5 Promise.reject 方法:(reason) => {}
(1) reason:失败的原因
说明:返回一个失败的 promise 对象
let p1 = Promise.reject(521) let p2 = Promise.reject(new Promise((resolve, reject) => { resolve('ok') }))
3.4.6 Promise.all 方法:(promises) => {}
(1) promises:包含 n 个 promise 的数组
说明:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败
不演示了,写的太累了,看字面意思就知道这个函数的作用了。
3.4.7 Promise.race 方法:(promises) => {}
(1) promises:包含 n 个 promise 的数组
说明:返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态
4. promise 的几个关键问题
一:如何改变 promise 的状态
let p = new Promise((resolve, reject) => { // 1. resolve 函数 resolve('ok') // pending ---> fulfilled // 2. reject 函数 reject('err') // pending ---> rejected // 3. 抛出错误 throw '出问题了'; })
二:一个 promise 指定多个成功/失败回调函数,都会调用吗?
当 promise 改变为对应状态时都会调用
let promise = new Promise((resolve, reject) => { resolve('Ok'); }) // 指定回调函数 promise.then(res => { console.log(res); }) promise.then(res => { alert(res); })
三:改变 promise 状态 和 指定回调函数谁先谁后?
(1):都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态再指定回调
正常情况
promise 执行器内部是 异步操作,所以是先指定回调,再改变状态
let promise = new Promise((resolve, reject) => { setTimeout(() => { // 再改变状态 resolve('Ok') }, 1000) }) // 先指定回调 promise.then(res => { // 但 res 结果的获得,必须要等异步执行结束,状态改变才能获取到 console.log(res); })
(2):如何先改状态再指定回调?
- 在执行器中直接调用 resolve() / reject()
- 延迟更长时间才调用 then()
let promise = new Promise((resolve, reject) => { // 同步操作,直接先改变状态 resolve('Ok'); }) // 再指定回调 promise.then(res => { console.log(res); })
四:promise.then() 返回的新 promise 的结果状态由什么决定
(1) 简单表达:由 then() 指定的回调函数执行
(2) 详细表达:
如果抛出异常,新 promise 变为 rejected,reason 为 抛出的异常
如果返回的是非 promise 的任意值,新 promise 变为 resolved, value为返回的值
- 如果返回的是另一个新 promise,此 promise 的结果就会成为 新 promise 的结果
let promise = new Promise((resolve, reject) => { resolve('Ok'); }) promise.then(res => { console.log(res); // 1. 抛出错误 // throw '出了问题' // 2. 返回结果非 promise 对象 return 123; // 3. 返回结果是promise 对象 return new Promise((resolve, reject) => { resolve('DDD'); }) })
五:promise 如何串连多个操作任务?
(1) promise 的 then() 内部返回一个新的 promise,可以 .then() 进行链式调用
(2) 通过 then 的链式调用串连 多个同步/异步任务
就最开始提出按顺序读取a.txt , b.txt, c.txt 终于可以解释了。
如何用promise 解决回调地狱
const fs = require('fs') let p1 = new Promise((resolve, reject) => { fs.readFile('a.txt', 'utf-8', (err, data) => { resolve(data) }) }) let p2 = new Promise((resolve, reject) => { fs.readFile('b.txt', 'utf-8', (err, data) => { resolve(data) }) }) let p3 = new Promise((resolve, reject) => { fs.readFile('c.txt', 'utf-8', (err, data) => { resolve(data) }) }) p1.then(r1 => { console.log(r1); return p2; }).then(r2 => { console.log(r2); return p3; }).then(r3 => { console.log(r3); })
六:promise 异常穿透
(1) 当使用 promise 的 then 链式调用时,可以在最后指定失败的回调
(2) 前面任何操作出了异常,都会传到最后失败的回调中处理
let promise = new Promise((resolve, reject) => { reject('Err'); }) let p = promise.then(res => { // console.log(111); throw '失败了'; }).then(value => { console.log(222); }).then(value => { console.log(333); }).catch(reason => { console.log(reason); })
七:中断 promise 链?
(1) 当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的回调函数
(2) 办法:在回调函数中返回一个 pendding 状态的 promise 对象
let promise = new Promise((resolve, reject) => { resolve('Ok'); }) let p = promise.then(res => { console.log(111); // 有且只有一个方式 // 回调函数执行的前提是 在状态改完之后才能执行。 // 这里返回的promise 状态是 pendding return new Promise(() => {}); }).then(value => { console.log(222); }).then(value => { console.log(333); }).catch(reason => { console.log(reason); })
5. 手写 Promise
5.1 定义整体结构
创建两个文件 index.html,promise.js
在 promise.js 写最基本的 promise 结构
function Promise(executor) { } // 添加 then 方法 Promise.prototype.then = function(onResolved, onRejected) { }
index.html 里引入 我们刚写的 promise.js
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!-- 引入 promise --> <script src="./promise.js"></script> </head> <body> <script> let p = new Promise((resolve, reject) => { resolve('Ok'); }) p.then(res => { console.log(res); }, reason => { console.log(reason); }) </script> </body> </html>
5.2 封装 resolve 和 reject 结构
promise.js 里的 代码
function Promise(executor) { // resolve 函数 function resolve(data) { } // reject 函数 function reject(data) { } // 同步调用 [执行器函数] executor(resolve, reject); }
5.3 resolve 和 reject 代码的实现
function Promise(executor) { // 添加属性 this.PromiseState = 'pending'; this.PromiseResult = null; // 保存实例对象的 this 的值 const that = this; // resolve 函数 function resolve(data) { // 1. 修改对象的状态 (promiseState) that.PromiseState = 'fulfilled'; // 2. 设置对象结果值 (promiseResult) that.PromiseResult = data; } // reject 函数 function reject(data) { // 1. 修改对象的状态 (promiseState) that.PromiseState = 'rejected'; // 2. 设置对象结果值 (promiseResult) that.PromiseResult = data; } // 同步调用 [执行器函数] executor(resolve, reject); } // 添加 then 方法 Promise.prototype.then = function(onResolved, onRejected) { }
5.4 throw 抛出异常改变状态
try { // 同步调用 [执行器函数] executor(resolve, reject); } catch (error) { // 修改 promise 对象状态为 失败 reject(error) }
5.5 promise 对象状态只能修改一次
// resolve 函数 function resolve(data) { // 来个if判断一下就行 if (that.PromiseState !== 'pending') return; // 1. 修改对象的状态 (promiseState) that.PromiseState = 'fulfilled'; // 2. 设置对象结果值 (promiseResult) that.PromiseResult = data; } // reject 函数 function reject(data) { // 判断状态 if (that.PromiseState !== 'pending') return; // 1. 修改对象的状态 (promiseState) that.PromiseState = 'rejected'; // 2. 设置对象结果值 (promiseResult) that.PromiseResult = data; }
5.6 then方法执行回调
// 添加 then 方法 Promise.prototype.then = function(onResolved, onRejected) { // 调用回调函数 PromiseState if (this.PromiseState === 'fulfilled') { onResolved(this.PromiseResult); } if (this.PromiseState === 'rejected') { onRejected(this.PromiseResult); } }
5.7 异步任务回调的执行
这部分我感觉就是promise 最核心关键的地方了。
在前面的执行器函数中,一直是同步执行的,所以在 then 方法中能直接获取到 PromiseState 和 PromiseResult的值。但现在要在 executor 执行器中进行异步函数调用了,而 then 方法中回调函数就不能直接获得 PromiseState 和 PromiseResult。要等异步函数执行结束后才能得到。而如何处理才能使得 then 方法 得到 executor 执行器中 异步函数产生的值?
官方的解决方法是:
在 then 方法中判断 promise 的状态,如果是 pending,说明异步函数还没执行结束,这时不能直接调用 then 方法中的回调函数,先把回调函数保存下来。如何保存?在 promise 构造函数中用一个属性保存。
function Promise(executor) { // 声明一个属性,用来保存 then 中的回调函数 this.callback = {} }
然后在 pending 状态时,保存回调函数
其实这时候 then 方法已经结束了,没有把回调函数进行调用,所以就先把回调函数存到 p 这个对象的 callback 上。在异步任务结束后,交给window 托管的 resolve 开始执行,这个 window 托管的函数使用了 p 对象中存着的 callback 函数。
index.html
<script> let p = new Promise((resolve, reject) => { // this 的判断是根据 . 号前面的对象决定的 // 这里直接是函数,resolve内部 this 指向 window // resolve('Ok'); setTimeout(() => { // resolve('Ok'); reject('err') }, 1000) }) p.then(res => { console.log(res); }, reason => { console.log(reason); }) console.log(p); </script>
promise.js
// 添加 then 方法 Promise.prototype.then = function(onResolved, onRejected) { // 判断 pending 状态 if (this.PromiseState === 'pending') { // 保存回调函数 this.callback = { onResolved: onResolved, onRejected: onRejected } } }
在 异步任务完成后,调用回调函数
// resolve 函数 function resolve(data) { // 判断状态 if (that.PromiseState !== 'pending') return; // 1. 修改对象的状态 (promiseState) that.PromiseState = 'fulfilled'; // 2. 设置对象结果值 (promiseResult) that.PromiseResult = data; // 调用成功的回调函数 if (that.callback.onResolved) { that.callback.onResolved(data); } } // reject 函数 function reject(data) { // 判断状态 if (that.PromiseState !== 'pending') return; // 1. 修改对象的状态 (promiseState) that.PromiseState = 'rejected'; // 2. 设置对象结果值 (promiseResult) that.PromiseResult = data; // 调用成功的回调函数 if (that.callback.onRejected) { that.callback.onRejected(data); } }
5.8 指定多个回调的实现
问题:
我们希望这两个回调都能执行,但保存第二个回调时会将第一个回调覆盖掉,所以原来的保存方法不行,需要修改。
修改前
let p = new Promise((resolve, reject) => { // this 的判断是根据 . 号前面的对象决定的 // 这里直接是函数,resolve内部 this 指向 window // resolve('Ok'); setTimeout(() => { // resolve('Ok'); reject('err') }, 1000) }) p.then(res => { console.log(res); }, reason => { console.log(reason); }) // 保存第二个回调,会将之前保存在 p 对象中 callback 的内容覆盖掉 // 所以前面的保存方法不行,需要修改 p.then(res => { alert(res); }, reason => { alert(reason); })
修改后
改用数组存 callback,再遍历把每个 回调函数都调用。
// 声明一个属性,用来保存 then 中的回调函数 this.callback = [] // resolve 函数 function resolve(data) { // 判断状态 if (that.PromiseState !== 'pending') return; // 1. 修改对象的状态 (promiseState) that.PromiseState = 'fulfilled'; // 2. 设置对象结果值 (promiseResult) that.PromiseResult = data; // 遍历调用成功的回调函数 that.callback.forEach(item => { item.onResolved(data); }); } // reject 函数 function reject(data) { // 判断状态 if (that.PromiseState !== 'pending') return; // 1. 修改对象的状态 (promiseState) that.PromiseState = 'rejected'; // 2. 设置对象结果值 (promiseResult) that.PromiseResult = data; // 遍历调用失败的回调函数 that.callback.forEach(item => { item.onRejected(data); }); } // 添加 then 方法 Promise.prototype.then = function(onResolved, onRejected) { // 调用回调函数 PromiseState if (this.PromiseState === 'fulfilled') { onResolved(this.PromiseResult); } if (this.PromiseState === 'rejected') { onRejected(this.PromiseResult); } // 判断 pending 状态 if (this.PromiseState === 'pending') { // 保存回调函数 this.callback.push({ onResolved: onResolved, onRejected: onRejected }); } }
5.9 同步修改状态 then 方法结果返回
这一块部分相当复杂,需要慢慢来理一下,后面异步的部分会更发杂(麻了)
前面的知识前提:
我们
在前面的 promise 中学到 p.then() 这个方法会返回一个 promise 对象, 返回的具体内容根 回调函数的 return 内容相关。
现在这一部分是先按同步的来,下一章内容是异步
先写整体骨架代码:
let p = new Promise((resolve, reject) => { resolve('Ok') }) let outerResult = p.then(value => { return 521; }, reason => { console.log(reason); }) console.log(outerResult);
我们先返回 非 promise 的值,返回 521。
在 promise.js 中 then() 方法 返回 一个 promise 对象
这是回调函数返回是 非 promise 的情况
Promise.prototype.then = function (onResolved, onRejected) { // 返回 promise return new Promise((resolve, reject) => { // 调用回调函数 PromiseState if (this.PromiseState === 'fulfilled') { // 所以这个result就是回调函数返回的结果 let result = onResolved(this.PromiseResult); // 如果返回的是 promise 对象 if (result instanceof Promise) { // 这里先不处理 } else { // 返回的是常规值就是成功 // 要将 outerResult 这个 promise 目前的状态 pending 改成fulfilled // 通过 resolve 就可以 resolve(result); } } }) }
结果
tips:
我把 p.then() 返回 的 promise 对象命名为 outerResult,在 p.then() 内的回调函数返回的 promise 对象命名为 result
这是回调函数返回是 promise 的情况,并且这个 promise 是成功的状态
let outerResult = p.then(value => { let result = new Promise((resolve, reject) => { resolve('Hello'); }) return result; }, reason => { console.log(reason); })
Promise.prototype.then = function (onResolved, onRejected) { // 返回 promise return new Promise((resolve, reject) => { // 调用回调函数 PromiseState if (this.PromiseState === 'fulfilled') { // 获取回调函数的结果 let result = onResolved(this.PromiseResult); // 如果返回的是 promise 对象 if (result instanceof Promise) { // result 执行的是成功,就要给外层的 outerResult 成功的效果 result.then(value => { // 这里 resolve 调用者是 outerResult // result 的结果就是 outerResult的结果 resolve(value); }, reason => { reject(reason) }) } else { // 返回的是常规值就是成功 // 通过 resolve 就可以 resolve(result); } } }) }
完整代码
Promise.prototype.then = function (onResolved, onRejected) { // 返回 promise return new Promise((resolve, reject) => { // 调用回调函数 PromiseState if (this.PromiseState === 'fulfilled') { try { // 获取回调函数的结果 let result = onResolved(this.PromiseResult); // 如果返回的是 promise 对象 if (result instanceof Promise) { // result 执行的是成功,就要给外层的 outerResult 成功的效果 result.then(value => { // 使 outerResult 的 状态 和 result 的状态一致 resolve(value); }, reason => { reject(reason) }) } else { // 返回的是常规值就是成功 // 要将这个 大 的 promise 目前的状态 pending 改成fulfilled // 通过 resolve 就可以 resolve(result); } } catch (error) { reject(error); } } if (this.PromiseState === 'rejected') { onRejected(this.PromiseResult); } // 判断 pending 状态 if (this.PromiseState === 'pending') { // 保存回调函数 this.callback.push({ onResolved: onResolved, onRejected: onRejected }); } }) }
5.10 异步修改状态 then 方法返回
这一部分就更复杂了。。。
因为 executor 内部是异步函数执行,所以在执行到 p.then() 时,p 的状态是 pending ,直接进入 .then() 方法的最后一个判断。说的太抽象了,来看下实际代码。
index.html
let p = new Promise((resolve, reject) => { setTimeout(() => { resolve('Err'); }, 1000) }) let outerResult = p.then(value => { console.log(value); // return 521; }, reason => { console.log(reason); }) console.log(outerResult);
promise.js
之前写的代码,在碰到 executor 里是异步函数时,操作是先把回调函数保存起来,等异步函数执行结束,调用 resolve 或 reject 时,再调用 回调函数。
现在因为 .then() 方法执行后要返回一个 promise 对象,如果直接简单的保存回调函数,返回的 promise 对象的状态一直是 pending,PromiseResult 这个属性也一直是 null。
所以要对保存回调函数进行魔改,让后面执行这个回调函数时,能修改 outerResult 的两个属性。
Promise.prototype.then = function (onResolved, onRejected) { // 返回 promise return new Promise((resolve, reject) => { // 判断 pending 状态 if (this.PromiseState === 'pending') { // 保存回调函数 this.callback.push({ onResolved: function (data) { // console.log('success'); // 执行成功的回调函数 // 获取回调函数返回结果 try { let res = onResolved(data); if (res instanceof Promise) { // 根据 回调函数返回的 promise 决定 res.then(value => { // 这个 回调函数 返回的 promise 内部调用的是 resolve resolve(value); }, reason => { reject(reason); }) } else { // 不是 promise 返回的就是正确 // 改变outerResult的状态 resolve(res); } } catch (error) { reject(error) } }, onRejected: function (data) { try { // 改变 promise 的状态为 rejected let res = onRejected(data); if (res instanceof Promise) { // 根据 回调函数返回的 promise 决定 res.then(value => { // 这个 回调函数 返回的 promise 内部调用的是 reject resolve(value); }, reason => { reject(reason); }) } else { // 改变outerResult的状态 reject(res); } } catch (error) { reject(error) } } }); } }) }