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

Promise 相关 #9

Open
liangbus opened this issue Nov 26, 2019 · 5 comments
Open

Promise 相关 #9

liangbus opened this issue Nov 26, 2019 · 5 comments
Labels
ES6 ES6 新特性 JavaScript

Comments

@liangbus
Copy link
Owner

liangbus commented Nov 26, 2019

基础知识点就不过多描述了,主要还是通过问题去发掘一些知识点

1. 关于 Promise 状态的理解

以下代码输出什么

new Promise((resolve, reject) => {
	console.log(1)
	resolve()
	console.log(2)
	reject()
	console.log(3)
}).then(() => {
	console.log(4)
}).catch(() => {
	console.log(5)
})

结果:
1
2
3
4

resolve 完之后的代码也会执行,但是 resove 之后,Promise 的状态已从 pending 改为 resolved,关于状态的转换,Promise 的 A+ 规范上面有相应描述

A promise must be in one of three states: pending, fulfilled, or rejected.

  • When pending, a promise: may transition to either the fulfilled or rejected state.
  • When fulfilled, a promise: must not transition to any other state. must have a value, which must not change.
  • When rejected, a promise: must not transition to any other state. must have a reason, which must not change.

即状态一旦更改,即不可逆,也不可变
所以即使 reject 了也不会进入 catch 方法

题目拓展 >>

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 0)
})
const p2 = p1.then(() => {
	throw new Error('something wrong!')
})
console.log(`p1 > `, p1)
console.log(`p2 > `, p2)

setTimeout(() => {
	console.log(`p1 > `, p1)
	console.log(`p2 > `, p2)
}, 0)

VM8247:9 p1 >  Promise {<pending>}
VM8247:10 p2 >  Promise {<pending>}
VM8443:7 Uncaught (in promise) Error: something wrong!
    at <anonymous>:7:8
p1 >  Promise {<resolved>: "success"}
VM8443:14 
p2 >  Promise {<rejected>: Error: something wrong!
    at <anonymous>:7:8}
@liangbus
Copy link
Owner Author

2. 假如有一个 Promise 在构造函数里面 throw 一个错误,然后后面先 catch 再执行 then, 那么 then 方法会被执行吗?为什么

new Promise(() => {
	throw Error('something happended!')
}).catch(err => {
	console.log(`err > `, err)
}).then(() => {
	console.log(`I am in then func`)
})
// 执行结果
err >  Error: something happended!
    at <anonymous>:2:8
    at new Promise (<anonymous>)
    at <anonymous>:1:1
VM425:6 I am in then func
Promise {<resolved>: undefined}

这是有一次面试遇到的一个问题,感觉这么问,结果大概率是会执行的,但是并不知道为什么[cry]

从结果导向,既然执行了 then ,那就看执行完 catch 方法后,返回是什么就知道了

new Promise(() => {
	throw Error('something happended!')
}).catch(err => {
	console.log(`err > `, err)
})
VM536:4 err >  Error: something happended!
    at <anonymous>:2:8
    at new Promise (<anonymous>)
    at <anonymous>:1:1
Promise {<resolved>: undefined}

注意这里返回结果是一个 Promise ,并且其状态是 resolved !!

仅执行构造函数,返回结果如下

new Promise(() => {
	throw Error('something happended!')
})
Promise {<rejected>: Error: something happended!
    at <anonymous>:2:8
    at new Promise (<anonymous>)
    at <anonymo…}

状态为 rejected 的 Promise

MDN 的解释是这样子的

pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。
image

至此,该问题就很清晰了

参考:
Promise - MDN

@liangbus
Copy link
Owner Author

liangbus commented Nov 29, 2019

关于 Promise 的链式调用?

下面的代码输出什么?

Promise.resolve(1).then((res) => { 
	console.log('then >> ', res)
	throw new Error('Wrong!!!!')
	return 2
}).catch((err) => {
	console.log('Error >> ', err)
	return 3
}).then((res) => {
	console.log('last then >>> ', res)
})
then >>  1
VM8925:6 Error >>  Error: Wrong!!!!
    at <anonymous>:3:8
VM8925:9 last then >>>  3

MDN 关于 Promise.resolve(value) 的描述

返回一个状态由给定value决定的Promise对象。如果该值是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定;否则的话(该value为空,基本类型或者不带then方法的对象),返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。通常而言,如果你不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象,这样就能将该value以Promise对象形式使用。

问题延伸

then 返回的是什么?如果返回一个异常会怎么样?

以下代码

Promise.resolve().then(() => {
	console.log('inside then~')
	return new Error('something WRONG!!')
}).then(res => {
	console.log('inside second then ~', res)
}).catch(err => {
	console.log('inside catch >> ', err)
})
VM260:2 inside then~
VM260:5 inside second then ~ Error: something WRONG!!
    at <anonymous>:3:9
Promise {<resolved>: undefined}

注意这里并不是 throw Error 而是 return Error
因为返回任意一个非 promise 的值都会被包裹成 promise 对象,即 return new Error('error!!!') 等价于 return Promise.resolve(new Error('error!!!'))。
正确触发 catch 的写法:

return Promise.reject(new Error('error!!!'))
throw new Error('error!!!')

再来看下面代码

Promise.resolve()
  .then(function success (res) {
    throw new Error('error')
  }, function fail1 (err) {
    console.error('fail in reject func: ', err)
  })
  .catch(function fail2 (err) {
    console.error('fail in catch: ', err)
  })

VM10390:8 fail in catch:  Error: error
    at success (<anonymous>:3:11)
Promise {<resolved>: undefined}

可以看出 then 方法中传入的 reject 方法没办法捕获到 resovlve 中抛出的异常,只能由后续的 catch 方法捕获到
再来修改一下看看

Promise.resolve()
  .then(function success (res) {
    throw new Error('error')
  }, function fail1 (err) {
    console.error('fail in reject func: ', err)
  }).then( function success2 (res) {
	console.log('succeed in second success func', res)
  }, function fail2 (err) {
	console.error('fail in second reject func', err)
  })

fail in second reject func Error: error
    at success (<anonymous>:3:11)
Promise {<resolved>: undefined}

此时第一个 then 中抛出的异常,被第二个 then 中的 reject 方法捕获到了
其实原因还是因为 then 执行完之后,返回的 Promise 状态是 rejected,所以导致后面触发的是 reject 方法,同级的 reject 方法无法捕获同级的 resolve 中的异常,这也是 Promise 的局限性之一,也就叫做被吃掉的异常,因为总会有可能存在无法捕获的异常

参考:
Promise 必知必会(十道题) - 石墨文档

@liangbus liangbus added ES6 ES6 新特性 JavaScript labels Dec 2, 2019
@liangbus
Copy link
Owner Author

练习:用 Promise 实现红绿灯问题
红灯三秒亮一次,绿灯一秒亮一次,黄灯2秒亮一次;如何让三个灯不断交替重复亮灯?

function red(){
  console.log('red');
}
function green(){
  console.log('green');
}
function yellow(){
  console.log('yellow');
}
function lightup(t, fn) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      fn()
      resolve()
    }, t)
  })
}

function round() {
  Promise.resolve().then(() => {
    return lightup(3000, red)
  })
  .then(() => {
    return lightup(1000, green)
  })
  .then(() => {
    return lightup(2000, yellow)
  })
  .then(() => {
    round()
  })
}

采用递归 + setTimeout 方式调用

@liangbus
Copy link
Owner Author

最后附上简单版本的 Promise/A+ 手写实现

@YuetTong
Copy link

YuetTong commented Mar 6, 2022

哥哥 你看我这个还有的救吗?除了开头那6秒多等,我想clearinterval当第一次执行时不等待,貌似不行。呜呜呜。。。。。。
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
let i = 0
setInterval(() => {
if (i === 0) {
clearInterval()
}
setTimeout(() => {
red()
setTimeout(() => {
green()
setTimeout(() => {
yellow()
}, 1000);
}, 2000);
}, 3000);
i++
}, 6000);

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

No branches or pull requests

2 participants