Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

9 - Promise #9

Open
8 tasks done
Linjiayu6 opened this issue Jul 8, 2020 · 11 comments
Open
8 tasks done

9 - Promise #9

Linjiayu6 opened this issue Jul 8, 2020 · 11 comments

Comments

@Linjiayu6
Copy link
Owner

Linjiayu6 commented Jul 8, 2020

  • 执行api / 基础题等 / try catch 是否有效等 问题
  • 实现 Promise / Promise.prototype.then
  • 实现 static Promise.race 谁快先返回谁 作业帮 promise.race 当得知返回 如何停止掉其他的请求 写代码
  • 实现 static Promise.all 并行异步
  • 实现 static Promise.resolve / static Promise.reject
  • 实现 串行异步 reduce实现
  • 实现 Promise.prototype.finally 阿里一面🔥🔥🔥🔥🔥
  • 实现 Promise.prototype.catch
@Linjiayu6
Copy link
Owner Author

Linjiayu6 commented Jul 8, 2020

resolve / reject 触发 then往后的内容
- resolve / reject 触发后续 then
- then(值) 穿透, 用的是 (data) => data 包装  [🌰 见题8]
- then(() => { return xxx })  return 被封装到了resolve里   [🌰 见题7]

参考题

题 1

new Promise(resolve => {
  console.log(1)
  resolve(3)
  console.log(4) // ?? 为什么这个会被执行
})
.then(num => console.log(num))

console.log(2)
// 结果: 1, 4, 2, 3

题 2

new Promise((resolve, reject) => {
  console.log(1) // 会执行
  resolve(3) // 只会走这里
  reject(300000)
  console.log(4) // 会执行
})
.then(
  num => console.log('success', num),
  num => console.log('error', num),
)
console.log(2)
// 1, 4, 2, success 3

题 3

🔥 非常的 ~ 有必要 多看

new Promise((resolve, reject) => {
  console.log(1)
  resolve(3)
  reject(300000)
  Promise.resolve().then(() => console.log(4))
})
.then(
  num => console.log('success', num),
  num => console.log('error', num),
)
console.log(2)
// 1, 2, 4, success 3 🔥

题 4

  • new Promise体内所有同步都会执行, resolve是将值放到 microqueue 队列中, 等待then被执行
  • 没有resolve 或 reject 是不会then下去的
new Promise ((resolve, reject) => {
  resolve(1)
  reject(2)
  resolve(3)
  console.log(4) // 先输出4
  return new Promise((resolve, reject) => {
    console.log('4-1') 
    // 直接执行 '4-1'
    // 没有resolve 不会then下去
  }).then(data => {
    console.log(data)
    resolve('4-1-1')
  })
})
.then(
  data => console.log('resolve', data),
  reason => console.log('reject', reason),
)
.catch(
  err => console.log('catch', err)
)
// 4, 4-1, resolve 1

题 5

上层嵌套里的先被执行

new Promise ((resolve, reject) => {
  resolve(1) //  1-then放到 microqueue队列中, 等待执行
  reject(2)
  resolve(3)
  console.log(4) // 输出1:  4

  return new Promise((resolve, reject) => { // 遇到Promise立刻执行
    console.log('4-1') // 输出2:  4-1
    resolve('from 4-1') // 放入队列中
  }).then(data => {
    console.log(data) // 输出3:  from 4-1
    resolve('4-1-1') // ???? 没有后续了 废弃
  })
})
.then(
  data => console.log('resolve', data), //  输出4: resolve 1
  reason => console.log('reject', reason),
)
.catch(
  err => console.log('catch', err)
)

// 4, 4-1,  from 4-1, resolve 1

题 6

setTimeout(function() { 
    console.log(1) 
}, 0); 
new Promise(function executor(resolve) { 
    console.log(2); 
    for( var i=0 ; i<10000 ; i++ ) { 
        i == 9999 && resolve();  // 这个是个同步任务哦~ 
    } 
    console.log(3); 
}).then(function() { 
    console.log(4); 
}); 
console.log(5);

// 2, 3, 5, 4, 1 

题 7

没有resolve 就不会继续then下去, return 相当于resolve

Promise.resolve(1)
.then((res) => {
    console.log(res)
    return 2; // 会继续往下面传递  resolve(2)
})
.catch((err) => { return 3; })
.then((res) => { console.log(res);  return 4})
.then((res) => { console.log(res); })
.then((res) => { console.log(res); }) // 没有从上面传下来的值 undefined

// 1, 2, 4, undefined

题 8

Promise.then 是一定要 传函数的, 如果不是函数, 就会发生值穿透现象。
所以 1 一直会被穿透 返回为1

Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)

题 9 🔥

const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
  resolve('success')
}, 1000);
});
 
const promise2 = promise1.then(() => {
  throw new Error('error!!!');
});
 
console.log('promise1', promise1);
console.log('promise2', promise2);

// 🔥
setTimeout(() => {
  console.log('promise1', promise1);
  console.log('promise2', promise2);
}, 2000);

image

@Linjiayu6
Copy link
Owner Author

Linjiayu6 commented Jul 8, 2020

并行异步 Promise.all

function _Promise_ () {}
_Promise_.all = function (queue) {
  let result = []
  let count = 0

  return new Promise((resolve, reject) => {
    queue.forEach((q, index) => {
      q
        .then(data => {
          result[index] = data
          count += 1
          if (count === queue.length) { resolve(result) }
        })
        .catch(err => reject(err))
    })
  })
}
var p1 = new Promise((resolve) => { setTimeout(() => { console.log(3000); resolve(3000) }, 3000) })
    p2 = new Promise((resolve) => { setTimeout(() => { console.log(0); resolve(0) }, 0) })
    // p3 = new Promise((resolve, reject) => { setTimeout(() => { console.log(5000); reject(5000) }, 5000) })
   p3 = new Promise((resolve, reject) => { setTimeout(() => { console.log(5000); resolve(5000) }, 5000) })

_Promise_.all([p1, p2, p3]).then(data => {
  console.log(data)
}).catch((err) => {
  console.log('error', err)
})

@Linjiayu6
Copy link
Owner Author

Linjiayu6 commented Jul 8, 2020

实现 Promise.race TODO 如何停止其他请求呢?

function PromiseRace (queue) {
  // 谁先返回用谁
  return new Promise((resolve, reject) => {
    // queue.forEach(q => {
    //   q
    //   .then(data => resolve(data))
    //   .catch(err => reject(err))
    // })

    // 同上
    queue.forEach(q => q.then(resolve).catch(reject))
  })
}
var p1 = new Promise((resolve) => { setTimeout(() => { console.log(3000); resolve(3000) }, 3000) })
    p2 = new Promise((resolve) => { setTimeout(() => { console.log(0); resolve(0) }, 0) })
    // p3 = new Promise((resolve, reject) => { setTimeout(() => { console.log(5000); reject(5000) }, 5000) })
    p3 = new Promise((resolve, reject) => { setTimeout(() => { console.log(5000); resolve(5000) }, 5000) })

PromiseRace([p1, p2, p3])
.then(data => { console.log('返回结果成功', data) })
.catch(error => { console.log('返回结果失败', error) })

@Linjiayu6
Copy link
Owner Author

Linjiayu6 commented Jul 8, 2020

实现 异步串行

A => B => C => D

// 串行机制
new Promise((resolve) => { 
  setTimeout(() => { 
    resolve(1000) 
  }, 1000) 
}).then((data) => {
  return new Promise((resolve) => {
    setTimeout(() => { 
      resolve(data + 1000) 
    }, data + 1000) 
  })
}).then((data) => {
  return new Promise((resolve) => {
    setTimeout(() => { 
      resolve(data + 1000) 
    }, data + 1000) 
  })
})

🔥 reduce 实现 前面.then(后面)

// 实现串行异步, 从上面传值往下
function serial (queue) {
  queue.reduce((accumulator, current) => {
   // 一样效果  return accumulator.then(data => current(data))
    return accumulator.then(current)
  }, Promise.resolve(1000))


 // queue.reduce((prev, next) => prev.then(next), Promise.resolve(0))
}
// 测试
var p1 = timer => new Promise((resolve) => {
  setTimeout(() => { 
    console.log(timer)
    resolve(timer + 1000) 
  }, timer + 1000)
})

serial([p1, p1, p1])

=

@Linjiayu6
Copy link
Owner Author

实现 Promise.catch

/**
 * 实现 Promise.catch 
 * 目标: 只接受reject, 是 Promise.reject的语法糖
 */
Promise.reject(1)
.then(
  data => console.log('onResolved', data),
  // 这里写的话, reject是匹配到了 onRejected, 不写的话 匹配到catch里面
  // err => console.log('onRejected', err)
)
.catch(err => console.log('onCatch', err))

Promise.reject 的语法糖

Promise.prototype._catch_ = function (callback) {
  return this.then(null, callback)
}

@Linjiayu6
Copy link
Owner Author

Linjiayu6 commented Jul 9, 2020

实现 Promise.finally

/**
 * 实现 Promise.finally 
 * 目标: 走个过场, 将值传递给下面 无论成功或失败, 值会直接穿透到下面去
 */

Promise.reject(1)
.then(
  data => console.log('onResolved', data),
  // 这里写的话, reject是匹配到了 onRejected, 不写的话 匹配到catch里面
  // err => console.log('onRejected', err)
)
.finally(data => console.log('finally 只是为了传值', data)) // undefined
.catch(err => console.log('onCatch', err))

Promise.resolve 和 Promise.reject 语法糖

Promise.prototype._finally_ = function (callback) {
  // 返回一个 Promise
  let P = this.constructor
  // 先去执行callback, 之后将值传给下个 promise
  return this.then(
    data => P.resolve(callback()).then(() => data),
    reason => P.resolve(callback()).then(() => { throw reason })
  )
}

@Linjiayu6
Copy link
Owner Author

Linjiayu6 commented Jul 29, 2020

Promise I

class Promise {
  constructor (executor) {
    this.status = 'PENDING'
    this.value = null
    this.reason = null
    this.resolveCallbacks = []

    executor(this._resolve.bind(this))
  }

  _resolve (value) {
    if (this.status === 'PENDING') {
      this.value = value // 保存结果
      this.status = 'FULFILLED' // 更新状态
      console.log('resolveCallbacks', this.resolveCallbacks)
      this.resolveCallbacks.forEach(q => q(value)) // 执行回调
    }
  }

  then (onResolvedFn) {
    if (this.status === 'PENDING') { // 将回调值放到回调队列中, 待执行
      this.resolveCallbacks.push(onResolvedFn)
    } else {
      // 执行
      onResolvedFn(this.value)
    }
    return this
  }
}

先去执行异步事件, then里面内容先去注册到队列中, 等着resolve后, 再去执行注册队列。

let p = new Promise(resolve => {
  console.log('同步执行');
  setTimeout(() => { resolve('开始执行回调了') }, 2000) // 1. 异步事件, 会去执行, 等待resolve
}).then(tip => { // 2. 还没有执行resolve, pending状态, 先去将回调函数放入队列中
  console.log('then1', tip);
}).then(tip => { // 3. 同上
  console.log('then2', tip);
});

/**
 * 4. 此时 this.resolveCallbacks = [f, f] 两个待执行
 * 5. 等到2s后, resolve(开始执行回调了)
 * 6. this.resolveCallbacks被执行
 * 7. 两个then有相同的输出, 是 '开始执行回调了'
 */

问题: 仅一个promise对象实例 再怎么then下去 返回的是一个结果,因为value已经存下来了

解决: 每次then都是 return new Promise 就好 新的实例

@Linjiayu6
Copy link
Owner Author

/**
 * 1. 通过实例状态判断
 *    if PENDING, 就then内容要注册放到队列里, 后续再执行
 *    else RESOLVED, 就直接执行then注册的回调函数
 * 
 * 2. then下去, 返回 this
 *    但是希望每个实例里的value值不同, 那就创建个新的 return new Promise
 *    - 1. PENDING 说明还在异步, 没有resolve/reject 提示执行呢, 那就存起来
 *    - 2. RESOLVED 那就立刻执行 1. 再链式调用then下去  2. resolve(data) 传给回调执行
 */

@Linjiayu6
Copy link
Owner Author

Linjiayu6 commented Jul 29, 2020

Promise 实现

  • 状态导向 正在 / 完成 / 拒绝
  • 只有 [完成 / 拒绝] 状态才会在then里直接执行回调
  • 否则 说明上一个还在异步执行中, 所以要将内容放到队列中,等待执行。
class Promise {
  constructor (executor) {
    this.status = 'PENDING'; // 初始化状态声明 PENDING RESOLVED REJECTED
    this.data = null; // 成功值
    this.reason = null; // 失败值
    this.resolvecbQueue = []; // 成功回调队列
    this.rejectcbQueue = []; // 失败回调队列

    executor(this._resolve.bind(this), this._reject.bind(this))
  }

  // 静态方法 Promise.resolve
  static resolve (value) {
    if (value instanceof Promise) return value
    return new Promise(resolve => resolve(value))
  }

  // 静态方法 Promise.reject
  static reject (reason) {
    return new Promise((resolve, reject) => reject(reason))
  }

  static all (queue) {
    return new Promise ((resolve, reject) => {
      var result = [], count = 0
      queue.forEach((q, i) => {
        q.then(d => {
          result[i] = d
          count += 1
          if (count === queue.length) {
            resolve(result)
          }
        })
      })
    })
  }

  static race (queue) {
    return new Promise (resolve => {
      queue.forEach((q) => {
        q.then(d => resolve(d))
      })
    })
  }

  // p.catch((err) => console.log(err))
  // 交给 _reject, 执行回调
  catch (onRejected) {
    return this.then(null, onRejected)
  }

  // 过一下, 不做任何处理, 状态都是 resolve
  finally (callback) {
    return this.then(
      value => Promise.resolve(callback()).then(() => value),
      reason => Promise.resolve(callback()).then(() => reason)
    )
  }

  _resolve (data) {
    // 异步结束了, 开始执行回调了
    if (this.status === 'PENDING') {
      this.data = data
      this.status = 'RESOLVED'
      this.resolvecbQueue.forEach(q => q(data))
    }
  }

  _reject (reason) {
    if (this.status === 'PENDING') {
      this.reason = reason
      this.status = 'REJECTED'
      this.rejectcbQueue.forEach(q => q(reason))
    }
  }

  then (onResolvedFn, onRejectedFn) {
    onResolvedFn = typeof onResolvedFn === 'function' ? onResolvedFn : function (value) {}
    onRejectedFn = typeof onRejectedFn === 'function' ? onRejectedFn : function (reason) {}


    // 还没有resolve, 只能先保存注册内容
    if (this.status === 'PENDING') {
      return new Promise((resolve, reject) => {
        this.resolvecbQueue.push(value => {
          var ret = onResolvedFn(value)
          if (ret instanceof Promise) {
            ret.then(resolve, reject)
          } else {
            resolve(value)
          }
        })
        // 同理 this.rejectcbQueue.push(reason => onRejectedFn(reason));
      })
    }

    // 状态变了 直接执行, 另外需要then下去
    if (this.status === 'RESOLVED') {
      return new Promise((resolve, reject) => {
        var ret = onResolvedFn(this.data)
        if (ret instanceof Promise) {
          ret.then(resolve, reject)
        } else {
          resolve(this.data)
        }
      })
    }

    if (this.status === 'REJECTED') {
      return new Promise((resolve, reject) => {
        var ret = onRejectedFn(this.reason)
        if (ret instanceof Promise) {
          ret.then(resolve, reject) // 继续then下去
        } else {
          reject(this.reason) // 直接返回数据
        }
      })
    }
  }
}
let p = new Promise(resolve => {
  console.log('#同步执行');
  setTimeout(() => { resolve('1') }, 2000)
}).then(data => {
  console.log('#then1#', data);
  return new Promise(resolve => {
    setTimeout(() => { resolve('2' + data) }, 2000)
  }).then(data => new Promise(resolve => {
    console.log('#then2#', data);
    setTimeout(() => { resolve('3' + data) }, 1000)
  }))
}).then(data => {
  console.log('#then3#', data);
  return data
}).then(data => {
  console.log('#then4#', data);
})
Promise.resolve(1000).then(d => new Promise(resolve => {
  console.log(d)
  setTimeout(() => { resolve(2000 + d) }, 1000)
})).then(console.log)

@Linjiayu6
Copy link
Owner Author

H5 Node AI DtoCode

@PstereoJW
Copy link

PstereoJW commented Dec 26, 2022

补充一下promise的实现:
then中调用onRejectedFnonResolvedFn的返回值需要进行处理,如果返回值不为thenable对象,需要包裹Promise.resolve(),如果返回值是thenable对象则递归处理thenable对象。
这里简单改了一下返回值有值时也是继续then下去。

    if (this.status === "REJECTED") {
      return new Promise((resolve, reject) => {
        var ret = onRejectedFn(this.reason);
        if (typeof ret !== "undefined") {
          Promise.resolve(ret).then(resolve, reject); // 继续then下去
          // ret为{then:promiseFun}对象没有处理
        } else {
          reject(this.reason); // 直接返回数据
        }
      });
    }

这种改法可以解决下面这个例子:

Promise.reject(1)
  .then((res) => {
    console.log(res);
    return 2;
  })
  .catch((err) => {
    console.log(err);
    return 3;
  })
  .then((res) => {
    console.log(res);
  });

这种改法不能解决下面这个例子:

Promise.reject(1)
  .then((res) => {
    console.log(res);
    return 2;
  })
  .catch((err) => {
    console.log(err);
    return { then: (resolve) => setTimeout(() => resolve(3), 2000) };
  })
  .then((res) => {
    console.log(res);
  });

另外一种能通过PromiseA+的实现,也可以参考这位大佬的实现 通过PromiseA+测试的Promise实现

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants