一、认识异步编程
1.同步模式和异步模式
Javascript设计初衷是为了在浏览器里满足网页交互的需要,所以设计为单线程模式,简单、安全,但如果某段代码执行很费时,就会造成后面代码的延迟执行(页面假死)。于是Js将任务的执行模式分成两种:同步模式、异步模式。
同步模式的执行顺序就是排队执行,前面的执行完毕,后面的再执行。
异步模式则是开启一个任务后,不会等待任务执行完毕才开始下一个任务,而是开启一个任务后就开始下一个任务。
同步或异步是说,执行环境提供的API是以同步还是异步的模式去工作。
2.异步模式图解
下图一的代码中,setTimeout 这个异步模式工作的API,其功能就是在某段时间后,向Javascirpt的消息队列中push一个任务(回调函数),当时机到了,就去执行回调函数。本段代码中,除setTimeout是异步模式工作的,其它的代码都是同步模式工作的。
2.1 EventLoop(事件循环机制)
Javascript的执行是单线程的,就是说一个时刻只能执行一个任务(一句代码),将要执行的代码会被放入执行栈(Call stack),每执行完毕一句,就从栈中清除。
如果遇到像setTimeout、setInterval这些异步执行的API,则会把其回调函数(按设定的时机)push入消息队列(就是待执行的任务队列)。
当目前执行栈(Call stack)中的代码都执行完毕,则EventLoop(事件循环机制)就会从消息队列中取出最早入队的任务,并把该任务代码放入执行栈,然后开始执行。
当执行栈内代码再次执行完毕,EventLoop(事件循环机制)就继续从消息队列中取出最早入队的任务,去执行。直至所有消息队列中的任务全部执行完毕。
简而言之:EventLoop就是在执行栈当前执行的代码运行完毕后,再去从消息队列中取出最早入队的一个任务执行,每个任务依次执行,直至全部任务执行完毕。
2.2 消息队列
消息队列用于存放异步模式API们的回调函数。每个回调函数都是一个待执行任务(一段待执行代码)。
2.3 宏任务和微任务
消息队列中的每个任务,都是宏任务,宏任务需要等待前面的宏任务依次执行后,(当EventLoop循环轮到自身时)自身再执行,setTimeout方法接收的回调函数和Dom事件回调就会作为宏任务去消息队列排队等待。
而为了提高效率,浏览器API中的Promise回调、MutationObserver和node环境的API process.nextTick 都会作为微任务执行,意思就是在当前执行栈中的代码一执行完毕,就立即执行(作为当前执行任务的微任务),而不用到消息队列去排队等待。


二、回调函数
回调函数是所有异步编程模式的基础,回调函数可以理解为一件很明确的想要做的事情,但是需要等待某个时机成熟才去做(执行)。
三、Promise的使用
一个Promise类型的对象,用于描述一个异步任务的最终结果。
Promse使用示例:
// Promise基本用法
const promise = new Promise(function (resolve, reject){
resolve(100) // 承诺达成
// reject(new Error('promise rejected')) // 承诺失败
})
promise.then(function(value){ // then接收的方法会进入回调队列
console.log('resolved:', value)
}, function(err){
console.log('rejected', err)
})
console.log('end') // end会先于上面promise的回调执行
/*
输出结果:
end
resolved: 100
*/
Promise本质上也是使用回调函数来定义异步任务结束后所需要执行的任务,嵌套使用的方式是使用Promise的常见错误
可借助于Promise then方法的链式调用的特点,保证异步任务的扁平化。
Promise对象的then 方法会返回一个全新的promise对象,后面的then方法就是在为前一个then 返回的Promise 注册回调方法,前面 then 方法中回调函数的返回值会作为后面 then 方法回调的参数。如果回调中返回的是Promise,那后面的then 方法的回调会等待它的结束。
Promise链式调用示例:
// Promise的链式调用示例
const promise = new Promise(function(resolve, reject){
resolve('111')
}).then((value) => {
console.log('222')
console.log(value)
return 'gagaga'
}).then((value) => {
console.log('333')
console.log(value)
})
/*
输出结果:
222
111
333
gagaga
*/
Promise.all静态方法的使用示例:
// Promise.all静态方法 按照异步任务的调用顺序获得各个任务的结果
function p1(){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 2000)
})
}
function p2(){
return new Promise((resolve, reject) => {
resolve('p2')
})
}
Promise.all(['a', 'b', p1(), p2(), 'c'])
.then(values => {console.log('values: ', values)}, reason => console.log(reason))
/*输出结果(2秒后输出):
values: [ 'a', 'b', 'p1', 'p2', 'c' ]
*/
Promise.resolve 方法,其作用就是把一个普通的值转换为promise对象;如果参数就是promise对象,则直接返回该对象。
Promise.resolve静态方案使用示例:
// Promise.resolve方法
function p1(){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p1 lalala')
}, 2000)
})
}
Promise.resolve(50)
.then(value => console.log(value)) // 相当于 .then(console.log)
Promise.resolve(p1())
.then(console.log)
/*
输出结果(立即输出第一行,2秒后输出第二行):
50
p1 lalala
*/
四、Generator异步方案和Async/Await
1. Generator异步方案
ES2015提供了Generator 生成器函数,允许一个函数分段执行。
A.生成器函数示例:
// Generator函数生成器示例1
function * generateId(){
let index = 0
while(true){
index++
yield index
}
}
const geneId = generateId()
const r1 = geneId.next()
console.log('r1: ', r1)
setTimeout(() => {
const r2 = geneId.next().value
console.log('r2: ', r2)
}, 1000)
/*
输出结果:
r1: { value: 1, done: false }
r2: 2
*/
B.假如有一个多步骤的生成器函数,需要逐个执行其中的每个异步步骤,则需要一个生成器函数的自动执行器:
生成器函数的自动执行器示例:
/*
Generator函数生成器的 自动执行器
*/
function ajax(type){
return new Promise((resolve, reject) => {
if(type == 'users'){
setTimeout(() => resolve(['Andy', 'Lily']), 2000)
} else if(type == 'posts'){
setTimeout(() => resolve(['Street', 'Street2']), 3000)
}
})
}
function * main(){
try{
const users = yield ajax('users')
console.log('users: ', users)
const posts = yield ajax('posts') // 需等待获取到users之后再执行
console.log('posts: ', posts)
} catch(e){
console.log(e)
}
}
//生成器函数的自动执行器
function co(generator){
const g = generator()
function handleResult(result){
if(result.done) return
result.value.then(data => {
handleResult(g.next(data))
}, error => {
g.throw(error)
})
}
handleResult(g.next())
}
co(main)
/*
输出结果(2秒后输出users,再过3秒输出posts):
users: [ 'Andy', 'Lily' ]
posts: [ 'Street', 'Street2' ]
*/
2.Async/Await - 语法层面的异步编程方案
相比Generator函数生成器,Async/Await 不需要手动去执行。
Async函数的返回值是一个promise对象,方便对整体流程的控制。
Await目前只能用于有Async描述的方法中。
Async/Await异步编程示例(将上例中的函数改为Async描述,yield关键词改为await即可):
// Async/Await异步编程示例
function ajax(type){
return new Promise((resolve, reject) => {
if(type == 'users'){
setTimeout(() => resolve(['Andy', 'Lily']), 2000)
} else if(type == 'posts'){
setTimeout(() => resolve(['Street', 'Street2']), 3000)
}
})
}
async function main(){
try{
const users = await ajax('users')
console.log('users: ', users)
const posts = await ajax('posts')
console.log('posts: ', posts)
} catch(e){
console.log(e)
}
}
const promise = main()
promise.then(() => {
console.log('整体结束')
})
/*
输出结果(2秒后输出users,再过3秒输出posts和整体结束):
users: [ 'Andy', 'Lily' ]
posts: [ 'Street', 'Street2' ]
整体结束
*/
五、手写Promise源码
1.对Promise的分析
const promise = new Promise((resolve, reject) => {
resolve('成功的值')
reject('失败原因')
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
1. Promise是一个类 这个类接收一个执行器(函数) 执行器会立即执行
这个执行器有两个参数:分别是resolve方法和reject方法
2. Promise有三种状态:成功-fulfilled 失败-rejected 等到-pending
pending -> fulfilled
pending -> rejected
一旦状态确定就不能更改
3. resolve 和 reject 函数是用来更改状态的
resolve: fulfilled
reject: rejected
4. then方法内部做的事就是判断当前Promise的状态
如果状态是成功则执行成功回调函数
如果状态时失败则执行失败回调函数
2.实现MyPromise核心
Step1:手写MyPromise-核心 构造器接收一个执行器函数 对象内部状态 resolve/reject方法 then方法
// Step1:手写MyPromise-核心 构造器接收一个执行器函数 对象内部状态 resolve/reject方法 then方法
// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor){ // new MyPromise() 接收一个执行器函数
executor(this.resolve, this.reject) // 执行器函数有两个参数:resolve和reject 是内部方法
}
status = PENDING // 标识该promise对象的状态
value = undefined // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
reason = undefined // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
resolve = value => { // 用于把promise对象的状态更改为fulfilled-成功
if(this.status !== PENDING) return; // promise对象状态只能由pending-等待 变成 成功或失败
this.status = FULFILLED
this.value = value // 成功了的数据保存在this.value中 后续传递给成功的回调函数
}
reject = reason => { // 用于把promise对象的状态更改为rejected-失败
if(this.status !== PENDING) return;
this.status = REJECTED
this.reason = reason // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
}
then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
if(this.status == FULFILLED){
successCallback(this.value)
} else if(this.status == REJECTED){
failCallback(this.reason)
}
}
}
// 测试1
const promise1 = new MyPromise((resolve, reject) => {
resolve(100)
})
promise1.then(val => {
console.log('success value: ', val)
}, rea => {
console.log('fail reason: ', rea)
})
/*
输出结果(立即输出): - 符合预期
success value: 100
*/
// 测试2
const promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 2000)
})
promise2.then(val => {
console.log('success value: ', val)
}, rea => {
console.log('fail reason: ', rea)
})
/*
输出结果 什么都没有输出:- 不符合预期
这是因为调用then方法的时候,promise对象还没有变成终态,所以then方法中还需要处理当前不是终态的情况
*/
由于上述这个思路代码,不满足promise状态延迟改变(异步)的需要,所以改写为如下:
Step2: 手写MyPromise-核心2 then方法调用时可能还未得到promise的终态
// 手写MyPromise-核心2 then方法调用时可能还未得到promise的终态
// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor){ // new MyPromise() 接收一个执行器函数
executor(this.resolve, this.reject) // 执行器函数有两个参数:resolve和reject 是内部方法
}
status = PENDING // 标识该promise对象的状态
value = undefined // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
reason = undefined // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
successCallback = undefined
failCallback = undefined
resolve = value => { // 用于把promise对象的状态更改为fulfilled-成功
if(this.status !== PENDING) return; // promise对象状态只能由pending-等待 变成 成功或失败
this.status = FULFILLED
this.value = value // 成功了的数据保存在this.value中 后续传递给成功的回调函数
this.successCallback && this.successCallback(this.value) // 如果有保存而未执行的成功回调 则此时执行
}
reject = reason => { // 用于把promise对象的状态更改为rejected-失败
if(this.status !== PENDING) return;
this.status = REJECTED
this.reason = reason // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
this.failCallback && this.failCallback(this.reason) // 如果有保存而未执行的失败回调 则此时执行
}
then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
if(this.status == FULFILLED){
successCallback(this.value)
} else if(this.status == REJECTED){
failCallback(this.reason)
} else { // 调用then方法时,promise还没有得到终态
// 要等到其得到终态后,再执行成功或失败的回调
// 先把回调都存储起来
this.successCallback = successCallback
this.failCallback = failCallback
}
}
}
// 测试1
const promise1 = new MyPromise((resolve, reject) => {
resolve(100)
})
promise1.then(val => {
console.log('success value: ', val)
}, rea => {
console.log('fail reason: ', rea)
})
/*
输出结果(立即输出): - 符合预期
success value: 100
*/
// 测试2
const promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 2000)
})
promise2.then(val => {
console.log('success value: ', val)
}, rea => {
console.log('fail reason: ', rea)
})
/*
输出结果(2秒后输出):- 符合预期
success value: 100
*/
promise对象的then方法可以多次调用:
// promise对象的then方法可以多次调用
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功的值')
}, 1500)
})
promise.then(value => {
console.log('value1 :', value)
})
promise.then(value => {
console.log('value2: ', value)
})
/*
输出结果(1.5秒后输出):
value1 : 成功的值
value2: 成功的值
*/
就是说 .then 方法可以多次调用,每次调用都可以设置成功或失败的回调函数,则改写为如下:
Step3: 手写MyPromise-核心3 promise的then方法可以添加多个
// 手写MyPromise-核心3 promise的then方法可以添加多个
// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor){ // new MyPromise() 接收一个执行器函数
executor(this.resolve, this.reject) // 执行器函数有两个参数:resolve和reject 是内部方法
}
status = PENDING // 标识该promise对象的状态
value = undefined // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
reason = undefined // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
successCallback = []
failCallback = []
resolve = value => { // 用于把promise对象的状态更改为fulfilled-成功
if(this.status !== PENDING) return; // promise对象状态只能由pending-等待 变成 成功或失败
this.status = FULFILLED
this.value = value // 成功了的数据保存在this.value中 后续传递给成功的回调函数
while(this.successCallback.length){ // 如果有保存而未执行的成功回调 则此时执行
const callback = this.successCallback.shift() // 回调函数数组的第一个先执行
callback(this.value)
}
}
reject = reason => { // 用于把promise对象的状态更改为rejected-失败
if(this.status !== PENDING) return;
this.status = REJECTED
this.reason = reason // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
// 如果有保存而未执行的失败回调 则此时执行
while(this.failCallback.length){
const callback = this.failCallback.shift()
callback(this.reason)
}
}
then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
if(this.status == FULFILLED){
successCallback(this.value)
} else if(this.status == REJECTED){
failCallback(this.reason)
} else { // 调用then方法时,promise还没有得到终态
// 要等到其得到终态后,再执行成功或失败的回调
// 先把回调都存储起来
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
}
// 测试3
const promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 2000)
})
promise2.then(val => {
console.log('success value: ', val)
}, rea => {
console.log('fail reason: ', rea)
})
promise2.then(val => {
console.log('another then: ', val)
})
/*
输出结果(2秒后输出):- 符合预期
success value: 100
another then: 100
*/
promise对象的then方法,会返回一个新的promise对象,故而.then方法可以链式调用,后面then方法的回调函数拿到的参数的值 是上一个then方法的回调函数的 返回值:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功的值')
}, 1000)
})
// then方法可以被链式调用,后面then方法的回调函数拿到的参数的值是上一个then方法的回调函数的返回值
const r1 = promise.then(value => {
console.log('value1 :', value)
return '另一个值'
}).then(value => { // 此处拿到的参数value 是上一个then方法的回调函数的 返回值
console.log('value2 :', value)
})
/*
输出结果(1秒后输出):
value1 : 成功的值
value2 : 另一个值
*/
Step4: 手写MyPromise-核心4 promise的then方法可以链式调用
// 手写MyPromise-核心4 promise的then方法可以链式调用
// 上一个then方法接收的回调函数的返回值 会传递给下一个then方法的回调函数作为参数
// then方法应该返回一个promise对象,这样才能链式调用
// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor){ // new MyPromise() 接收一个执行器函数
executor(this.resolve, this.reject) // 执行器函数有两个参数:resolve和reject 是内部方法
}
status = PENDING // 标识该promise对象的状态
value = undefined // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
reason = undefined // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
successCallback = []
failCallback = []
resolve = value => { // 用于把promise对象的状态更改为fulfilled-成功
if(this.status !== PENDING) return; // promise对象状态只能由pending-等待 变成 成功或失败
this.status = FULFILLED
this.value = value // 成功了的数据保存在this.value中 后续传递给成功的回调函数
while(this.successCallback.length){ // 如果有保存而未执行的成功回调 则此时执行
const callback = this.successCallback.shift() // 回调函数数组的第一个先执行
callback(this.value)
}
}
reject = reason => { // 用于把promise对象的状态更改为rejected-失败
if(this.status !== PENDING) return;
this.status = REJECTED
this.reason = reason // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
// 如果有保存而未执行的失败回调 则此时执行
while(this.failCallback.length){
const callback = this.failCallback.shift()
callback(this.reason)
}
}
then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
let promise2 = new MyPromise((resolve, reject) => { // 执行器内的代码立即执行
if(this.status == FULFILLED){
let x = successCallback(this.value) // 上一个promise对象回调函数的返回值 需要传递给下一个promise对象的回调函数
resolve(x)
} else if(this.status == REJECTED){
failCallback(this.reason)
} else { // 调用then方法时,promise还没有得到终态
// 要等到其得到终态后,再执行成功或失败的回调
// 先把回调都存储起来
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
})
return promise2
}
}
// 测试3
const promise = new MyPromise((resolve, reject) => {
resolve(100)
})
promise.then(val => {
console.log('success value: ', val)
return 500
}).then(val => {
console.log('another then: ', val)
})
/*
输出结果(立即输出):- 符合预期
success value: 100
another then: 500
*/
在上述Step4代码中,then方法的链式调用逻辑里,仅处理了前一个then调用时已经得到成功终态的情况,没有处理前一个then调用时尚未得到终态的情况(比如前一个then的回调函数返回一个promise对象,则下一个then应该等待该promise得到终态,并把这个返回的promise对象的值传递给一下个then的回调函数)
Step5: 手写MyPromise-核心5 then的链式调用 处理前一个then调用若返回promise 而promise尚未得到终态的情况
// 手写MyPromise-核心5 处理前一个then调用时尚未得到终态的情况
// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor){ // new MyPromise() 接收一个执行器函数
executor(this.resolve, this.reject) // 执行器函数有两个参数:resolve和reject 是内部方法
}
status = PENDING // 标识该promise对象的状态
value = undefined // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
reason = undefined // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
successCallback = []
failCallback = []
resolve = value => { // 用于把promise对象的状态更改为fulfilled-成功
if(this.status !== PENDING) return; // promise对象状态只能由pending-等待 变成 成功或失败
this.status = FULFILLED
this.value = value // 成功了的数据保存在this.value中 后续传递给成功的回调函数
while(this.successCallback.length){ // 如果有保存而未执行的成功回调 则此时执行
const callback = this.successCallback.shift() // 回调函数数组的第一个先执行
callback()
}
}
reject = reason => { // 用于把promise对象的状态更改为rejected-失败
if(this.status !== PENDING) return;
this.status = REJECTED
this.reason = reason // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
// 如果有保存而未执行的失败回调 则此时执行
while(this.failCallback.length){
const callback = this.failCallback.shift()
callback()
}
}
then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
let promise2 = new MyPromise((resolve, reject) => { // 执行器内的代码立即执行
if(this.status == FULFILLED){
let x = successCallback(this.value) // 上一个promise对象回调函数的返回值 需要传递给下一个promise对象的回调函数
// 这里需要判断上一个promise对象回调函数的返回值x是普通值 还是另一个promise对象
// 如果是普通值,则直接返回x
// 如果是promise对象,则应等待该promise对象执行完毕(得到终态)
// 该promise对象执行完毕的结果 如果是成功,则把成功的值传递给下一个then方法的成功回调
// 如果是失败,则把失败的原因传递给一下一个then方法的失败回调
handlePromiseReturned(x, resolve, reject)
} else if(this.status == REJECTED){
let x = failCallback(this.reason)
handlePromiseReturned(x, resolve, reject)
} else { // 调用then方法时,promise还没有得到终态
// 要等到其得到终态后,再执行成功或失败的回调
// 先把回调都存储起来
this.successCallback.push(() => {
let x = successCallback(this.value)
handlePromiseReturned(x, resolve, reject)
})
this.failCallback.push(() => {
let x = failCallback(this.reason)
handlePromiseReturned(x, resolve, reject)
})
}
})
return promise2
}
}
function handlePromiseReturned(x, resolve, reject){
if(x instanceof MyPromise){
x.then(value => {
resolve(value) // then方法将要返回的promise对象的 resolve方法
}, reason => {
reject(reason) // then方法将要返回的promise对象的 reject方法
})
} else {
resolve(x)
}
}
// 测试3
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('一个值')
}, 1000)
})
const other = function() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('另一个值')
}, 2000)
})
}
promise.then(val => {
console.log('success value: ', val)
return other() // other() 是一个promise对象 则下一个then将等待它得到终态后再执行
}).then(val => {
console.log('another then: ', val)
})
/*
输出结果(1秒后输出第一行,再过2秒输出第二行):- 符合预期
success value: 一个值
another then: 另一个值
*/
3.实现功能全面的Promise
在上面2.部分,我们按照如下思路,逐步实现了Promise的核心代码:
Step1: 手写MyPromise-核心 构造器接收一个执行器函数 promise对象内部状态 resolve/reject方法 then方法
Step2: 手写MyPromise-核心2 then方法调用时可能还未得到promise对象的终态
Step3: 手写MyPromise-核心3 promise的then方法可以添加多个(多次调用)
Step4: 手写MyPromise-核心4 promise的then方法可以链式调用
Step5: 手写MyPromise-核心5 then的链式调用 处理前一个then调用若返回promise 而该promise尚未得到终态的情况
下面,为实现一个功能全面的Promise,将为其继续添加如下Promise的特性:
禁止then方法的回调返回自身、
内部的错误捕获、
将then方法的参数变为可选参数(如果一个.then方法无回调函数传入,则其后面有回调函数的then方法来处理拿到的成功的值或失败的原因)、
Promise.all静态方法、
Promise.resolve静态方法、
promise对象的finally方法、
promise对象的catch方法。
Promise的finally方法示例:
// Promise的finally方法示例
function users(){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(['Andy', 'Heiby'])
}, 1000)
})
}
users().then(value => {
console.log('value: ', value)
return '123'
}, reason => {
console.log('reason: ', reason)
}).finally(() => { // finally接收一个最终回调方法 但finally方法拿不到promise最终的状态和值
console.log('finally')
return '456'
}).then(value => { // finally()后面的then()拿到的数据 与没有finally()时是一样的
console.log('another then, value: ', value)
})
/*
输出结果(1秒后输出):
value: [ 'Andy', 'Heiby' ]
finally
another then, value: 123
*/
功能全面的MyPromise对象示例:
// 手写MyPromise-核心
/*
Step1: Promise是一个类 其构造器接收一个执行器函数 promise对象内部状态 resolve/reject方法 then方法
Step2: then方法调用时可能还未得到promise对象的终态
Step3: promise的then方法可以添加多个(多次调用)
Step4: promise的then方法可以链式调用
Step5: then的链式调用 处理前一个then调用若返回promise 而该promise尚未得到终态的情况
Step6: 禁止then方法的回调返回自身、
内部的错误捕获、
将then方法的参数变为可选参数(如果一个then方法无回调函数传入,
则其后面有回调函数的then方法来处理拿到的成功的值或失败的原因)、
Promise.all静态方法、
Promise.resolve静态方法、
promise对象的finally方法、
promise对象的catch方法
*/
// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor){ // new MyPromise() 接收一个执行器函数
try{ // 内部的错误捕获
executor(this.resolve, this.reject) // 执行器函数有两个参数:resolve和reject 是内部方法
} catch(e) {
this.reject(e)
}
}
status = PENDING // 标识该promise对象的状态
value = undefined // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
reason = undefined // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
successCallback = []
failCallback = []
resolve = value => { // 用于把promise对象的状态更改为fulfilled-成功
if(this.status !== PENDING) return; // promise对象状态只能由pending-等待 变成 成功或失败
this.status = FULFILLED
this.value = value // 成功了的数据保存在this.value中 后续传递给成功的回调函数
while(this.successCallback.length){ // 如果有保存而未执行的成功回调 则此时执行
const callback = this.successCallback.shift() // 回调函数数组的第一个先执行
callback()
}
}
reject = reason => { // 用于把promise对象的状态更改为rejected-失败
if(this.status !== PENDING) return;
this.status = REJECTED
this.reason = reason // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
// 如果有保存而未执行的失败回调 则此时执行
while(this.failCallback.length){
const callback = this.failCallback.shift()
callback()
}
}
then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
successCallback = successCallback ? successCallback : value => value // 将then方法的参数变为可选参数
failCallback = failCallback ? failCallback : reason => { throw reason } // 将then方法的参数变为可选参数
let promise2 = new MyPromise((resolve, reject) => { // 执行器内的代码立即执行
if(this.status == FULFILLED){
setTimeout(() => {
try { // 内部的错误捕获
let x = successCallback(this.value) // 上一个promise对象回调函数的返回值 需要传递给下一个promise对象的回调函数
// 这里需要判断上一个promise对象回调函数的返回值x是普通值 还是另一个promise对象
// 如果是普通值,则直接返回x
// 如果是promise对象,则应等待该promise对象执行完毕(得到终态)
// 该promise对象执行完毕的结果 如果是成功,则把成功的值传递给下一个then方法的成功回调
// 如果是失败,则把失败的原因传递给一下一个then方法的失败回调
handlePromiseReturned(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
}, 0)
} else if(this.status == REJECTED){
setTimeout(() => {
try{ // 内部的错误捕获
let x = failCallback(this.reason)
handlePromiseReturned(x, resolve, reject)
} catch(e){
reject(e)
}
}, 0)
} else { // 调用then方法时,promise还没有得到终态
// 要等到其得到终态后,再执行成功或失败的回调
// 先把回调都存储起来
this.successCallback.push(() => {
setTimeout(() => {
try{ // 内部的错误捕获
let x = successCallback(this.value)
handlePromiseReturned(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
}, 0)
})
this.failCallback.push(() => {
setTimeout(() => {
try{ // 内部的错误捕获
let x = failCallback(this.reason)
handlePromiseReturned(promise2, x, resolve, reject)
} catch(e){
reject(e)
}
}, 0)
})
}
})
return promise2
}
finally = (callback) => { // finally接收一个回调函数 在promise得到终态后执行
// finally后面仍可以链式调用then() 后面的then()会等待finally回调函数执行完毕(得到终态)
this.then(value => {
// callback()
return MyPromise.resolve(callback()) // MyPromise.resolve把一个值转换成promise
.then(() => value) // 把promise成功的值返回
}, reason => {
return MyPromise.resolve(callback()).then(() => { throw reason })
})
}
catch = failCallback => {
return this.then(undefined, failCallback)
}
static all = (array) => { // Promise.all方法的返回值是一个promise对象
return new MyPromise((resolve, reject) => {
let results = []
let count = 0
function addResult(key, result){
results[key] = result
count++
if(count == array.length){
resolve(results)
}
}
for(let i = 0; i < array.length; i++){
const current = array[i]
if(current instanceof MyPromise){
current.then(value => {
addResult(i, value)
}, reason => {
reject(reason) // array中的任何一个promise失败,则Promise.all返回的promise就是状态失败
})
} else {
addResult(i, current)
}
}
})
}
static resolve = value => { // Promise.resolve方法把传入的值包装成promise对象 如果参数就是promise对象,则直接返回
if(value instanceof MyPromise) return value
return new MyPromise((resolve, reject) => {
resolve(value)
})
}
}
function handlePromiseReturned(promise2, x, resolve, reject){
if(x instanceof MyPromise){
if(promise2 === x) return reject(new Error('禁止then方法的回调返回自身')); // 禁止then方法的回调返回自身
x.then(value => {
resolve(value) // then方法将要返回的promise对象的 resolve方法
}, reason => {
reject(reason) // then方法将要返回的promise对象的 reject方法
})
} else {
resolve(x)
}
}
/* 测试 */
function users(){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(['Andy', 'Heiby'])
}, 1000)
})
}
var promise = new MyPromise((resolve, reject) => {
resolve('成功的值')
})
// then()方法参数可选
/*
const r1 = promise.then(console.log, console.log)
const r2 = promise.then().then().then(console.log, console.log)
*/
/*
输出结果(立即输出):
成功的值
成功的值
*/
// Promise.all静态方法
/*
function p1(){
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 2000)
})
}
function p2(){
return new MyPromise((resolve, reject) => {
resolve('p2')
})
}
var promiseAll = MyPromise.all(['a', 'b', p1(), p2(), 'c'])
.then(values => {
console.log(values)
}, reason => {
console.log('error: ', reason)
})
*/
/*
输出结果(2秒后输出):
[ 'a', 'b', 'p1', 'p2', 'c' ]
*/
本文 完。
308

被折叠的 条评论
为什么被折叠?



