简述
Promise是ES6中的新的异步语法,解决了回调嵌套的问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| new Promise((resolve)=>{ setTimeout(()=>{ resolve(1) },1000) }).then(val =>{ console.log(val); return new Promise((resolve)=>{ setTimeout(()=>{ resolve(2) },1000) }) }).then(val => { console.log(val); })
|
实现状态切换
- promise实例有三个状态,
pending
,fulfilled
,rejected
- promise实例在构造是可以传入执行函数,执行函数有两个形参
resolve
,reject
可以改变promise的状态,promise的状态一旦改变后不可再进行改变。
- 执行函数会在创建promise实例时,同步执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; class Promise2 { constructor(executor){ this.status = PENDING this.value = null this.reason = null const resolve = (value) => { if (this.status === PENDING) { this.value = value; this.status = FULFILLED; } } const reject = (reason) => { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; } } try { executor(resolve,reject) }catch (e) { reject(e) }
} } let p = new Promise2((resolve,reject)=>{resolve(1)})
|
实现then
异步执行
promise实例可以调用then
方法并且传入回调:
如果调用then
时,Promise实例是fulfilled
状态,则马上异步执行传入的回调。
如果调用then
时,Promise实例是pending
状态,传入的回调会等到resolve后再异步执行
例子:
1 2 3 4 5 6 7 8 9 10 11
| let p = new Promise((resolve, reject)=>{ console.log(1); resolve(2) console.log(3); }) p.then((val)=>{ console.log(val); })
|
1 2 3 4 5 6 7 8
| let p = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve(1) },2000) }) p.then((val)=>{ console.log(val); })
|
思路:需要用回调先保存到队列中,在resolve
后异步执行队列里的回调,在then
时判断实例的状态再决定是将回调推入队列,还是直接异步执行回调:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; class Promise2 { constructor(executor){ this.status = PENDING this.value = null this.reason = null this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.status === PENDING) { this.value = value; this.status = FULFILLED; setTimeout(()=>{ this.onFulfilledCallbacks.forEach(fn => fn(this.value)); }) } } const reject = (reason) => { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; setTimeout(()=>{ this.onFulfilledCallbacks.forEach(fn => fn(this.reason)); }) } } try { executor(resolve,reject) }catch (e) { reject(e) }
} then(onFulfilled, onRejected) { if (this.status === FULFILLED) { setTimeout(()=>{ onFulfilled(this.value); }) } if (this.status === REJECTED) { setTimeout(()=>{ onRejected(this.reason); })
} if (this.status === PENDING) { this.onFulfilledCallbacks.push(onFulfilled); this.onRejectedCallbacks.push(onRejected); } } }
|
resolve
Promise实例的情况
resolve
的值有可能也是个promise实例,这时候就要用前述实例自己resolve
的值
1 2 3 4 5 6 7 8 9 10
| let p = new Promise((resolve,reject) =>{ resolve(new Promise((resolve2,reject2)=>{ setTimeout(()=>{ resolve2(1) },1000) })) }) p.then((val)=>{ console.log(val); })
|
因此需要在promise1的resolve
函数中进行判断,是promise实例则在这个promise实例(promise2)后接一个then
,并且将promise1的resolve
作为回调传入promise2的then
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; class Promise2 { constructor(executor){ this.status = PENDING this.value = null this.reason = null this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (value instanceof this.constructor) { value.then(resolve, reject); return } if (this.status === PENDING) { this.value = value; this.status = FULFILLED; setTimeout(()=>{ this.onFulfilledCallbacks.forEach(fn => fn(this.value)); }) } } const reject = (reason) => { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; setTimeout(()=>{ this.onFulfilledCallbacks.forEach(fn => fn(this.reason)); }) } } try { executor(resolve,reject) }catch (e) { reject(e) }
} then(onFulfilled, onRejected) { if (this.status === FULFILLED) { setTimeout(()=>{ onFulfilled(this.value); }) } if (this.status === REJECTED) { setTimeout(()=>{ onRejected(this.reason); })
} if (this.status === PENDING) { this.onFulfilledCallbacks.push(onFulfilled); this.onRejectedCallbacks.push(onRejected); } } } let p = new Promise2((resolve,reject) =>{ resolve(new Promise2((resolve2,reject2)=>{ setTimeout(()=>{ resolve2(1) },1000) })) }) p.then((val)=>{ console.log(val); })
|
实现链式调用
then
可以链式调用,而且前一个then
的回调的返回值,如果不是promise实例,则下一个then
回调的传参值就是上一个then
回调的返回值,如果是promise实例,则下一个then
回调的传参值,是上一个then
回调返回的promise实例的解决值(value)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| let p = new Promise((resolve,reject) =>{ setTimeout(()=>{ resolve(1) },1000) }) p.then(val => { console.log(val); return new Promise((resolve) => { setTimeout(()=>{ resolve(2) },1000) }) }).then(val => { console.log(val); return 3 }).then(val => { console.log(val); })
|
既然能够链式调用,那么then
方法本身的返回值必定是一个Promise实例。那么返回的promise实例是不是自身呢?答案显而易见:不是。如果一个promise的then方法的返回值是promise自身,在new一个Promise时,调用了resolve方法,因为promise的状态一旦更改便不能再次更改,那么下面的所有then便只能执行成功的回调,无法进行错误处理,这显然并不符合promise的规范和设计promise的初衷。
因此 then
方法会返回一个新的promise实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; class Promise2 { constructor(executor){ this.status = PENDING this.value = null this.reason = null this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (value instanceof this.constructor) { value.then(resolve, reject);
return } if (this.status === PENDING) { this.value = value; this.status = FULFILLED; setTimeout(()=>{ this.onFulfilledCallbacks.forEach(fn => fn(this.value)); }) } } const reject = (reason) => { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; setTimeout(()=>{ this.onFulfilledCallbacks.forEach(fn => fn(this.reason)); }) } } try { executor(resolve,reject) }catch (e) { reject(e) }
} then(onFulfilled, onRejected) { const promise2 = new this.constructor((resolve, reject) => { if (this.status === FULFILLED) { setTimeout(()=>{ try { let callbackValue = onFulfilled(this.value); resolve(callbackValue); }catch(error) { reject(error) } }) } if (this.status === REJECTED) { setTimeout(()=>{ try { let callbackValue= onRejected(this.reason); resolve(callbackValue); } catch (error) { reject(error); } })
} if (this.status === PENDING) { this.onFulfilledCallbacks.push(() => { try { let callbackValue = onFulfilled(this.value); resolve(callbackValue); }catch (error) { reject(error) } }); this.onRejectedCallbacks.push(() => { try { let callbackValue = onRejected(this.reason); resolve(callbackValue); } catch (error) { reject(error); } }); } }) return promise2; } }
|
实现其他方法
- catch
- resolve
- reject
- all
- race
方法演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| let p = new Promise((resolve, reject) => { reject(1) }) p.catch(reason => { console.log(reason); })
let p = Promise.resolve(1)
let p = Promise.reject(1)
let p = Promise.all([ new Promise(resolve => { setTimeout(() => { resolve(1) }, 1000) }), new Promise(resolve => { setTimeout(() => { resolve(2) }, 2000) }), new Promise(resolve => { setTimeout(() => { resolve(3) }, 3000) }), ]) p.then(val => { console.log(val); })
let p = Promise.race([ new Promise(resolve => { setTimeout(() => { resolve(1) }, 1000) }), new Promise(resolve => { setTimeout(() => { resolve(2) }, 2000) }), new Promise(resolve => { setTimeout(() => { resolve(3) }, 3000) }), ]) p.then(val => { console.log(val); })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
| const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; class Promise2 {
static resolve(value) { if (value instanceof this) { return value; } return new this((resolve, reject) => { resolve(value); }); };
static reject(reason) { return new this((resolve, reject) => reject(reason)) }; static all(promises){ return new this((resolve, reject) => { let resolvedCounter = 0; let promiseNum = promises.length; let resolvedValues = new Array(promiseNum); for (let i = 0; i < promiseNum; i += 1) { Promise2.resolve(promises[i]).then( value => { resolvedCounter++; resolvedValues[i] = value; if (resolvedCounter === promiseNum) { return resolve(resolvedValues); } }, reason => { return reject(reason); }, );
} }); }; static race(promises){ return new this((resolve, reject) => { if (promises.length === 0) { return; } else { for (let i = 0, l = promises.length; i < l; i += 1) { Promise2.resolve(promises[i]).then( data => { resolve(data); return; }, err => { reject(err); return; }, ); } } }); } constructor(executor) { this.status = PENDING this.value = null this.reason = null this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (value instanceof this.constructor) { value.then(resolve, reject);
return } if (this.status === PENDING) { this.value = value; this.status = FULFILLED; setTimeout(() => { this.onFulfilledCallbacks.forEach(fn => fn(this.value)); }) } } const reject = (reason) => { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; setTimeout(() => { this.onFulfilledCallbacks.forEach(fn => fn(this.reason)); }) } } try { executor(resolve, reject) } catch (e) { reject(e) }
}
then(onFulfilled, onRejected) { const promise2 = new this.constructor((resolve, reject) => { if (this.status === FULFILLED) {
setTimeout(() => { try { let callbackValue = onFulfilled(this.value); resolve(callbackValue); } catch (error) { reject(error) } }) } if (this.status === REJECTED) { setTimeout(() => { try { let x = onRejected(this.reason); resolve(x); } catch (error) { reject(error); } })
} if (this.status === PENDING) { this.onFulfilledCallbacks.push(() => { try { let callbackValue = onFulfilled(this.value); resolve(callbackValue); } catch (error) { reject(error) } }); this.onRejectedCallbacks.push(() => { try { let callbackValue = onRejected(this.reason); resolve(callbackValue); } catch (error) { reject(error); } }); } }) return promise2; }
catch(onRejected) { return this.then(null, onRejected); } }
|
macrotask和mirotask
所谓macroTask
(宏任务)是指将任务排到下一个事件循环,microTask
(微任务)是指将任务排到当前事件循环的队尾,执行时机会被宏任务更早。Promise的标准里没有规定Promise里的异步该使用哪种,但在node和浏览器的实现里都是使用的miroTask
(微任务)

1 2 3 4 5 6 7
| setTimeout(() => { console.log(1); }, 0) let p = Promise.resolve(2) p.then((val) => { console.log(val); })
|
宏任务api包括:setTimeout
,setInterval
,setImmediate(Node)
,requestAnimationFrame(浏览器)
,各种IO操作,网络请求
微任务api包括:process.nextTick(Node)
,MutationObserver(浏览器)
MutaionObserver
演示:
1 2 3 4 5 6 7 8
| let observer = new MutationObserver(()=>{ console.log(1); }) let node = document.createElement('div') observer.observe(node, { childList: true }) node.innerHTML = 1
|
利用MutaionObserver
封装一个微任务执行函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| let nextTick = (function () { let callbacks = [] function nextTickHandler() { let copies = callbacks.slice(0) callbacks = [] for (let i = 0; i < copies.length; i++) { copies[i]() } }
let counter = 1 let observer = new MutationObserver(nextTickHandler) let node = document.createElement('div') observer.observe(node, { childList: true }) return function (cb) { callbacks.push(cb) counter = (counter + 1) % 2 node.innerHTML = counter } })()
|