8000
We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
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
现代 JavaScript 开发中,异步编程是一个很重要的话题,Promise 的出现为 JavaScript 异步编程提供了优雅的标准解决方案
Promise
本篇文章我们将循序渐进理解 Promise 核心知识
我们都知道,JavaScript 是单线程的,如果把线程想象成一个快递员(镇上唯一的快递员),那么
同步模式:你必须按顺序挨家挨户的送货,每送到一户,都要等待收件人签收,在当前收件人完成签收前,你无法前往下一户
基于这样的情况,不难看出同步模式的特点在于:
异步模式:快递员将包裹统统放在驿站,每个包裹都有一个取件码,收件人凭取件码取件,它们之前互不影响
同样的,异步模式的特点在于:
记住,同步与异步的本质区别在于:是否会阻塞当前执行流程
JavaScript 中的异步概念,更多指的是单线程 + 任务队列管理异步任务的执行顺序
注意,JavaScript 是单线程的,要避免将异步和多线程混淆,异步是同时处理多个任务,就好比一个人在边吃饭边看电视,而不是两个人吃饭看电视
回调地狱是在异步操作中因多层嵌套的回调函数导致的代码结构问题,它的表现方式就像是俄罗斯套娃,一层一层往下套,特别是在处理多个顺序依赖的异步操作时,也就是下一层的操作,需要基于上一层的数据
function double(value,successCallback){ setTimeout(()=>{ successCallback(value * 2) },1000) } // 回调地狱 double(10,(firstResult)=>{ console.log(firstResult);// 20 double(firstResult,(secondResult)=>{ console.log(secondResult); // 40 double(secondResult,(thirdResult)=>{ console.log(thirdResult); // 80 }) }) })
上面这段代码,在一秒后将打印出数字 20,再一秒后打印 40,最后是 80
回调地狱的核心问题在于其代码的可读性、可维护性难以言喻,就如同打了个死结!
ECMAScript 6 依据社区广泛流行的 Promise/A+ 规范并加以完善支持,在此基础上推出了 Promise 对象,为异步编程模式提供了标准方案
Promise 对象是一个构造函数,可以通过 new 操作符来创建它的实例,它接收一个执行器函数(executor)作为参数,比如:
new
const p1 = new Promise(()=>{}) console.log(p1);
在控制台打印 p1 实例,你会发现存在一个 [[PromiseState]] 属性,这个属性的值范围可以是三种状态:
p1
[[PromiseState]]
pending
fulfilled
rejected
这三种状态中,pending 是最初始的状态,也就是没有做任何影响状态的操作
但要注意的是,一个 Promise 对象的状态一旦发生改变,比如从 pending 转为 fulfilled、pending 转为 rejected 后,就已经落定,是不可逆的
并且,Promise 状态不能通过 JS 检测到,也无法进行修改
上面说到,Promise 实例化时接收一个执行器函数,而这个执行器函数又接收两个函数作为参数,一般是 resolve 和 reject
resolve
reject
在 TypeScript 中,表现为:
interface PromiseConstructor { //... new <T>(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>; }
resolve 翻译为解决的意思,可以将 Promise 对象的状态从 pending(待定) 转变到 fulfilled(已完成),并将结果(value)放在参数中传递出去
pending(待定)
fulfilled(已完成)
const p1 = new Promise((resolve,reject)=>{ resolve('成功!') }) console.log(p1);
相应的,reject 翻译为拒绝的意思,可以将 promise 状态从pending(待定) 转变到 rejected(已拒绝),并将理由(reason)放在参数中传递出去
promise
rejected(已拒绝)
const p1 = new Promise((resolve,reject)=>{ reject('失败') }) console.log(p1);
Promise 的实例上可以访问三种方法,这些方法是在 Primise.prototype 上定义的
Primise.prototype
先聊一聊重要的 then() 方法,它为 Promise 对象的 fulfilled(完成) 状态、rejected(拒绝) 状态提供处理程序,接收两个参数:
then()
fulfilled(完成)
rejected(拒绝)
onFulfilled
resolve(value)
value
onRejected
reject(reason)
reason
onFulfilled 和 onRejected 这两个参数在 then() 中是可选的,只写单个参数也可以
它的 TypeScript 类型表现为:
interface Promise<T> { //... then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>; }
理论了解后,来看看下面这个例子:
const p1 = new Promise((resolve,reject)=>{ if(/**条件 */){ resolve('成功值') // 触发 then 的 onFulfilled }else{ reject(new Error('失败')) // 触发 then 的 onRejected } }) p1.then( (res)=>{ console.log(res); // 成功值 }, (err)=>{ console.log(err); // Error 对象 } )
then() 方法会返回一个新的 Promise 实例,这意味着 then() 可以形成链式调用,也就是在 then() 之后再接 then()
并且,上一个 then() 可以通过 return 将值传递给下一个 then() ,但请注意,这里有一些规则:
return
undefined
看下面的例子:
const p1 = new Promise((resolve,reject)=>{ resolve('成功值') // 触发 then 的 onFulfilled }) .then(res=>{ console.log('首次then接收:', res) // 首次then接收: 成功值 return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('链式') // 异步改变状态为fulfilled(1秒后) },1000) }) }) .then(res=>{ // 前序Promise解决后才执行(等待1秒) console.log('异步then接收:', res) // 异步then接收: 链式 return '第二个then的值' // 同步 }) .then(res=>{ console.log(res); // 第二个then的值 })
catch() 方法用于执行 Promise 状态为 rejected(拒绝) 的回调函数
catch()
interface Promise<T> { //... catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>; }
在上文介绍的 then() 方法中,我们说它接收 onFulfilled 和 onRejected 两个参数
但在一般的实践中,我们通常使用 catch() 来充当 onRejected 处理程序的角色,因为 catch() 方法实际上是 then() 第二个参数 onRejected 程序的语法糖
并且,catch() 和 then() 一样,返回一个 Promise 对象
const p1 = new Promise((resolve,reject)=>{ reject(new Error('失败值')) }) p1 .then(res=>{ // 当状态为fulfilled时 console.log(res); }) .catch(err=>{ // 当状态为rejected时 console.log(err); })
finally() 方法的特点是不管 Promise 状态如何变化,都会执行的操作
finally()
它接收一个参数:
onFinally
fulfilled(成功)
TypeScript类型表现为:
interface Promise<T> { //... finally(onfinally?: (() => void) | undefined | null): Promise<T>; }
和前面的两个方法一样,finally() 方法也返回一个 Promise
还记得在回调地狱章节的示例代码吗?现在,有了 Promise 后,我们有了更棒的解决方案:
const asyncDouble = (value,successCallback)=>{ return new Promise((resolve)=>{ setTimeout(()=>{ resolve(value * 2) },1000) }) } asyncDouble(10) .then(res => { console.log(res); return asyncDouble(res) }) // 20 .then(res => { console.log(res); return asyncDouble(res) }) // 40 .then(res => { console.log(res); return asyncDouble(res) }) // 80
总结一下本文的内容,在开头我们通过快递员的例子讲解了同步和异步的概念:
然后,我们举例说明了在没有 Promise 之前,异步函数多层嵌套带来的可阅读性、可维护性差问题(回调地狱)
正式介绍 Promise 对象时,我们介绍了其基本语法和三个状态,并可以通过执行器函数切换状态,还介绍了 Promise.prototype 原型对象带给实例的三个方法 then()、catch()、finally()
Promise.prototype
本文已收录至《参透JavaScript系列》,全文地址:我的 GitHub 博客 | 掘金专栏
对文章内容有任何疑问、建议,或发现有错误,欢迎交流和指正
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
现代 JavaScript 开发中,异步编程是一个很重要的话题,
Promise
的出现为 JavaScript 异步编程提供了优雅的标准解决方案本篇文章我们将循序渐进理解
Promise
核心知识同步和异步的概念理解
我们都知道,JavaScript 是单线程的,如果把线程想象成一个快递员(镇上唯一的快递员),那么
同步模式:你必须按顺序挨家挨户的送货,每送到一户,都要等待收件人签收,在当前收件人完成签收前,你无法前往下一户
基于这样的情况,不难看出同步模式的特点在于:
异步模式:快递员将包裹统统放在驿站,每个包裹都有一个取件码,收件人凭取件码取件,它们之前互不影响
同样的,异步模式的特点在于:
记住,同步与异步的本质区别在于:是否会阻塞当前执行流程
JavaScript 中的异步概念,更多指的是单线程 + 任务队列管理异步任务的执行顺序
Promise 之前的回调地狱(Callback Hell)
回调地狱是在异步操作中因多层嵌套的回调函数导致的代码结构问题,它的表现方式就像是俄罗斯套娃,一层一层往下套,特别是在处理多个顺序依赖的异步操作时,也就是下一层的操作,需要基于上一层的数据
回调地狱的核心问题在于其代码的可读性、可维护性难以言喻,就如同打了个死结!
Promise 对象
ECMAScript 6 依据社区广泛流行的 Promise/A+ 规范并加以完善支持,在此基础上推出了 Promise 对象,为异步编程模式提供了标准方案
Promise 的基本用法
Promise
对象是一个构造函数,可以通过new
操作符来创建它的实例,它接收一个执行器函数(executor)作为参数,比如:在控制台打印
p1
实例,你会发现存在一个[[PromiseState]]
属性,这个属性的值范围可以是三种状态:pending
(待定) :初始状态,既没有完成,也没有被拒绝fulfilled
(已完成):意味着操作成功完成rejected
(已拒绝):意味着操作失败这三种状态中,
pending
是最初始的状态,也就是没有做任何影响状态的操作但要注意的是,一个
Promise
对象的状态一旦发生改变,比如从pending
转为fulfilled
、pending
转为rejected
后,就已经落定,是不可逆的并且,
Promise
状态不能通过 JS 检测到,也无法进行修改通过执行器函数(executor)来切换状态
上面说到,
Promise
实例化时接收一个执行器函数,而这个执行器函数又接收两个函数作为参数,一般是resolve
和reject
在 TypeScript 中,表现为:
resolve
翻译为解决的意思,可以将Promise
对象的状态从pending(待定)
转变到fulfilled(已完成)
,并将结果(value)放在参数中传递出去相应的,
reject
翻译为拒绝的意思,可以将promise
状态从pending(待定)
转变到rejected(已拒绝)
,并将理由(reason)放在参数中传递出去Promise 的实例方法
Promise
的实例上可以访问三种方法,这些方法是在Primise.prototype
上定义的Promise.prototype.then
先聊一聊重要的
then()
方法,它为Promise
对象的fulfilled(完成)
状态、rejected(拒绝)
状态提供处理程序,接收两个参数:onFulfilled
:当状态为fulfilled
时执行此回调函数,接收一个参数,参数值来于resolve(value)
中传递的value
值onRejected
:当状态为rejected
时执行此回调函数,接收一个参数,参数值来于reject(reason)
调用时传递的reason
(原因,理由)它的 TypeScript 类型表现为:
理论了解后,来看看下面这个例子:
then()
方法会返回一个新的Promise
实例,这意味着then()
可以形成链式调用,也就是在then()
之后再接then()
并且,上一个
then()
可以通过return
将值传递给下一个then()
,但请注意,这里有一些规则:then()
的参数是undefined
then()
,并将值传递Promise
实例,那么等到此Promise
状态为fulfilled(完成)
或rejected(拒绝)
时将值/理由传递看下面的例子:
Promise.prototype.catch
catch()
方法用于执行Promise
状态为rejected(拒绝)
的回调函数它的 TypeScript 类型表现为:
在上文介绍的
then()
方法中,我们说它接收onFulfilled
和onRejected
两个参数但在一般的实践中,我们通常使用
catch()
来充当onRejected
处理程序的角色,因为catch()
方法实际上是then()
第二个参数onRejected
程序的语法糖并且,
catch()
和then()
一样,返回一个Promise
对象Promise.prototype.finally
finally()
方法的特点是不管Promise
状态如何变化,都会执行的操作它接收一个参数:
onFinally
:在状态为fulfilled(成功)
、rejected(拒绝)
时都会触发的回调函数,但要注意,这个回调函数不接收任何参数值TypeScript类型表现为:
和前面的两个方法一样,
finally()
方法也返回一个Promise
Promise 解决回调地狱
还记得在回调地狱章节的示例代码吗?现在,有了 Promise 后,我们有了更棒的解决方案:
总结
总结一下本文的内容,在开头我们通过快递员的例子讲解了同步和异步的概念:
然后,我们举例说明了在没有
Promise
之前,异步函数多层嵌套带来的可阅读性、可维护性差问题(回调地狱)正式介绍
Promise
对象时,我们介绍了其基本语法和三个状态,并可以通过执行器函数切换状态,还介绍了Promise.prototype
原型对象带给实例的三个方法then()
、catch()
、finally()
参考资料
参透JavaScript系列
本文已收录至《参透JavaScript系列》,全文地址:我的 GitHub 博客 | 掘金专栏
交流讨论
对文章内容有任何疑问、建议,或发现有错误,欢迎交流和指正
The text was updated successfully, but these errors were encountered: