手写promise
一种异步的解决方案, 参考
Promise代码基本结构
function Promise(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
function resolve(){
}
function reject(){
}
}
module.exports = Promise
state保存的是当前的状态,在Promise状态发展只有以下两种模式且不可逆:
从上可知,状态只能由pending变为resolved
(执行resolve)或pending变为rejected
(执行reject)
这就引出了resolve和reject的功能
function Promise(executor){
this.state = 'pending'
this.value = undefined
this.reason = undefined
function resolve(value){
this.state = 'resolved'
this.value = value
}
function reject(reason){
this.state = 'rejected'
this.reason = reason
}
executor(resove, reject)
}
then方法的实现
当Promise的状态发生了改变,不论是成功或是失败都会调用then方法
let p = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(1)
},50)
})
p.then(data=>{console.log(data)})
可以得出then的方法,由于then方法是每个实例所以共有,因此可以将其写在原型链上:
- 接受2个参数,成功的回调
onFulfilled
和onRejected
- 两个函数分别在
resolved
状态下和rejected
状态下执行
- 两个函数分别在
Promise.prototype.then = function(onFulfilled, onRejected){
if(this.state === 'resolved'){
onFulfilled(this.value)
}
if(this.state === 'rejected'){
onRejected(this.reason)
}
}
当你写出了以上的代码,在同步的情况下执行完全没有问题. 但是在异步的情况下,流程如下:
- 函数执行
new Promise
,当遇到setTimeout
时,会将其推入一个异步队列中 - 然后执行p.then: 浏览器会顺着原型链找到
Promise.prototype.then
发现此时的状态是pending
,故不做任何处理,结束同步处理 - 开始执行异步队列里面的
setTimeout
,执行resovle(1)
.
异步方法的实现
处理异步方法的关键是,在Promise.prototype.then
方法中,当状态为pending
时,可以将处理函数作为变量存储起来,这样当异步过后,执行resolve
时,可以在内存中找到相应的处理函数并对其进行执行.
这就需要我们修改Promise构造函数
function Promise(exector){
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = [] // 用于存储成功的回调
this.onRejectedCallbacks = [] // 用于存储失败的回调
function resolve(value){
if(this.state === 'pending'){
this.state = 'resolved'
this.value = value
this.onResolvedCallbacks.forEach(resolved=> resolved(value))
}
}
function reject(reason){
if(this.state === 'pending'){
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(rejected=> rejected(reason))
}
}
}
// 修改 Promise.prototype.then
Promise.prototype.then = function (onFulfilled, onRejected){
if(this.state === 'resolved'){
onFulFilled(this.value)
}
if(this.state === 'rejected'){
onRejected(this.reason)
}
// 异步: 将回调函数放入内存中.
if(this.state === 'pending'){
if(typeof onFulfilled === 'function'){
this.onResolvedCallbacks.push(onFulfilled)
}
if(typeof onRejected === 'function'){
this.onRejectedCallbacks.push(onRejected)
}
}
}
当涉及到异步,使用this时要绝对小心
在使用setTimeout,里面回调使用的函数中的this是指向window的(通过debugger调试可以获得).这就需要在一开始时,将指向实例的this保存下来.
function Promise(exector){
let _this = this
_this.state = 'pending'
_this.value = undefined
_this.reason = undefined
_this.onResovledCallbacks = []
_this.onRejectedCallbacks = []
function resolve(value){
_this.state = 'resolved'
_this.value = value
_this.onResolvedCallbacks.forEach(resolved => resolved(value))
}
function reject(reason){
_this.state = 'rejected'
_this.reason = reason
_this.onRejectedCallbacks.forEach(rejected=> rejected(reason))
}
exector(resolve, reject)
}
Promise.prototype.then = function(onFulfilled, onRejected){
if(this.state === 'resolved'){
onFulfilled(this.value)
}
if(this.state === 'rejected'){
onRejected(this.reason)
}
if(this.state === 'pending'){
if(typeof onFulfilled === 'function'){
this.onResolvedCallbacks.push(onFulfilled)
}
if(typeof onRejected === 'function'){
this.onRejectedCallbacks.push(onRejected)
}
}
}
快排
思路: 将每次的第一个元素作为基准,将比基准小的放在left数组中,比基准大的放在right数组中.然后返回[left, pivot,right]
function qSort(arr){
let pivot,left = [],right = []
function loop(arr){
pivot = arr[0]
left = []
right = []
for(let i=0, len = arr.length; i < len; i++){
if(arr[i] > pivot){
right.push(arr[i])
} else{
left.push(arr[i])
}
}
return loop(left).concat(pivot, loop(right))
}
return loop(arr)
}
冒泡排序
思路:
- 记录待排序的数组长度 len
- 当len大于0时,会进入循环
- 会使用nextLen保存下次循环的长度.(为undefined会跳出循环)
- 每次循环都从第0号位开始,最后到len.
- 比较左右2边的元素
- 左 > 右: 则交换左右两边的位置,并将nextLen置为当前的下标
- 每次到循环结束,都会将len设为nextLen
function bubbleSort(arr){
let len = arr.length;
while(len > 0){
let tmp,nextLen
for(let i =0 ; i< len; i++){
if(arr[i]>arr[i+1]){
tmp = arr[i]
arr[i] = arr[i+1]
arr[i+1] = tmp
nextLen = i
}
len = nextLen
}
}
return arr
}
单例模式实现 发布/订阅 模式
-
单例模式即只有一个实例
-
发布订阅模式,即使用
addDep
添加依赖.使用notify
class Observer{
constructor(){
this.events ={}
}
addDep(tag, handler){
if(Array.isArray(this.events[tag])){
// 是一个数组
this.events[tag].push(handler)
}else{
this.events[tag] = [handler]
}
}
notify(tag, params){
this.events[tag].forEach(fn => fn(params))
}
}
以上实现了一个简单的观察者模式,下面使用单例模式对其进行改造.
简单的说就是在使用new Observer
时,返回的是同一个实例.给构造函数添加一个获取单例的方法
class Observer{
constructor(){
this.events = {}
this.instance = null
}
}
Observetr.getInstance = function (){
if(this.instance == null){
this.instance = new Observer
}
return this.instance
}
let o1 = Observetr.getInstance()
let o2 = Observetr.getInstance()
console.log(o1 === o2) // true