前言
不知道你有没有注意过早上的起床顺序呢,像是作者本来起床的顺序是:
拉开被子刷牙喝咖啡上班有注意到吗这些事情都是一个一个执行,相信应该没有人可以一边刷牙一边喝咖啡吧!
人脑是如此,电脑也不意外同时间一个执行绪能做的也就是一件事情,
JS 之中要做到有顺序的处理 Function 其中有几个方式 callback function、Promise、 为什么会发生 callback Hell,在本章节都会提到!
废话不多说我们一起往下走吧!
Callback Function
我们先来介绍不可不知道的 Callback Function。
当我们在一般撰写 Funciton 时,呼叫三件事情会是这样呼叫的。
const A = function () {}const B = function () {}const C = function () {}A()B()C()
可以看到执行顺序是 A() -> B() -> C()。
原因是这三个 Function 都是立即执行所以会按照顺序完成。
但如果这三件事情有一个 Function 出现了延迟会发生什么事情呢?
const A = function () { setTimeOut(()=>{},1000)}const B = function () {}const C = function () {}A()B()C()
执行顺序变成 B() -> C() -> A()。
那如果我们想要让执行顺序变 C()-> A() -> B() ,那 Callback Function 会是您的好选择。
Callback Function 主要带来的效益是 可以控制执行顺序的程式
。
而什么是 Callback Function 呢
Callback: 将一 Function 透过
参数
的方式传进另一 Function 之中。
Callback Function:被当成参数传递
的 Function 称作 Callback Function。
让可以直接看到例子。
/*** params {object} Breakfask* params {function} TakeBus*/const eatBreakfast = (breakfast, takeBus) => { //- 万恶小黄瓜不吃了不上班了! if(小黄瓜 in breakfast) return takeBus()}//- takeBus 就是 Callback Function
在了解 Callback Function 之后,我们可以改写一下上面的例子,我们预期执行顺序: C()-> A() -> B() 。
const A = function (func) { setTimeOut(()=>{func()},1000)}const B = function () {}const C = function () {}A(B())C()
这样就可以做到你想的事情搂!! 这样的作法经常被应用于 Ajax,因为 Ajax 是向外部进行请求时需等待资讯,并判断下一步应该透过哪一个 Function 来进行处理。
但如果今天 CallBack Function 非常的多层就会看到这样哩。
function helloIsme() { console.log("Hello is me");}doSomething(result => { doSomething(result, (_r) => { doSomething(_r, (_fr) => { console.log("Here's Johnny") }, helloIsme); }, helloIsme);}, helloIsme);
Johnny 就会出现(误)
没有拉 Johnny 不会出现只会有维护这段程式码的人会出现,当层数变多还可以顺便打波动拳呢,终于可以用上这张图片了。
这样的多层结构被称作于 Callback Hell,这样就让维运/接手人员折寿,所以 Promise 这东西出来拯救世人了。
Promise
Promise 物件代表一个即将完成、或失败的非同步操作,以及它所产生的值。 引用自Promise - JavaScript | MDN
换句话说 Promise 是一个非同步程序,当程序完成并成功时可以接应成功资料,反之失败时可以接应失败的物件。
那上面提到的 callback function 产生的问题 callback hell 就可透过状态进行反馈。
doSomething((result) => { doSomething(result, (_r) => { doSomething(_r, (_fr) => { console.log("Here's Johnny") }, helloIsme); }, helloIsme);}, helloIsme);//- 就会变成doSomething() .then(()=>{reutrn doSomething()}) .then(()=>{return doSomething()}) .then(()=>{ console.log("Here's Johnny") }) .catch(()=>{})
是不是变的相当简洁呢。
在学习前我们来了解一下 Promise 物件,此物件在执行中会有以下几种状态:
搁置(pending):初始状态,不是 fulfilled 与 rejected。
实现(fulfilled):表示操作成功地完成。
拒绝(rejected):表示操作失败了。
在 Promise 结果为 fulfilled 时代表 Promise resolve,透过 .then() 的方式可以编写成功该做何事。
在 Promise 结果为 rejected 时代表 Promise reject,透过 .catch() 的方式可以编写失败该做何事。
使用方式
Promise 是建构函式
,需要透过 new Promise()
来进行建置,除此之外还需要给予 Function ,告知 Promise 需如何进行成功与失败的判断,function 内容必须有两个参数 resolve
, reject
,分别代表成功与失败,可以看见下方例子。
const _promise = new Promise((resolve,reject)=>{//- todo something 成功resolve()//- todo something 失败reject()})* resolve 为成功取得的资料时,回传资料。* reject 则会在失败时回传 error object。
使用上会有几个 function 来表达此 Promise 状态。
.then()
: 接获 resolve 结果时会执行。.catch()
: 接获 reject 结果时会执行。.finally()
: 当此 Promise 所有 .then / .catch 完成后执行。_promise .then((res)=>{console.log('todo success thing')}) .catch((err)=>{console.log('todo error thing')}) .finally(()=>{console.log('todo finally thing')})
如果文字上看得不太清楚的话可以直接看下图。
再来我想要特别提到 Promise 中很重要的概念,链结(Promise Chain)
,如果想要使用链结的话需要在 then 中进行 return 一个 Promise,而下一个的 then 就会是 return 的结果。
需要特别注意: Promise 可以有很多的 then ,但是只能有一个 catch 。
Promise.resolve,Promise.reject
透过此方法来建置 Promise 的话,此 Promise 结果已经决定。
Promise.resolve 此结果必定成功。
Promise.reject 此结果必定失败。
const _promise = new Promise((resolve,reject)=>{ resolve('success') reject('error')})//- 只会成功不会失败 'success'_promise.resolve((res)=>{ console.log(res.data) })//- 只会失败不会成功 'error'_promise.reject((err)=>{ console.log(err) })
Promise.all
此方法用于多个 Promise 一同执行,并且等待所有 Promise 后才会一同返回,注意当有一个 Promise reject ,就会进入 catch ,必须全部回传成功(resolve)才会进入 then()。。
const A = new Promise((resolve,rejcet)=>{resolve(1)})const B = new Promise((resolve,rejcet)=>{resolve(2)})const C = new Promise((resolve,rejcet)=>{resolve(3)})const D = new Promise((resolve,rejcet)=>{reject('error')})Promise.all([A,B,C]).then(([resA,resB,resC])=>{ console.log(resA,resB,resC)}).catch(error=>{console.log(error)})//- console: 1, 2, 3Promise.all([A,B,D]).then(([resA,resB,resD])=>{ console.log(resA,resB,resD)}).catch(err=>{ console.log(error)})//- 'error'
也因为有一个 reject 就会进入 catch,所以会没有办法知道是哪一个 Promise 错误导致无法侦错。
Promise.allSettled
虽与 Promise.all 一样是,多个 Promise 进行同时处理,并等待完成后一起回传。
每一个回传都会包含 {status,value,reason}
const A = new Promise((resolve,rejcet)=>{resolve(1)})const B = new Promise((resolve,rejcet)=>{resolve(2)})const C = new Promise((resolve,rejcet)=>{resolve(3)})const D = new Promise((resolve,rejcet)=>{reject('error')})Promise.all([A,B,C,D]).then((res)=>{ console.log(res)}).catch(error=>{console.log(error)})//- console: { status: 'fulfilled', value: '1' }, { status: 'fulfilled', value: '2' }, { status: 'fulfilled', value: '3' },{ status: 'rejected', reason: 'error' }
常见疑问
Q1. Ajax 与 Promise 的差异?
Ans: Ajax 是处理 Client 与 Server 之间的沟通方式; Promise 是处理非同步的语法。举个例子来比喻 Ajax 是人与人的沟通技巧,Promise 则是人沟通所说的语言。
引用
MDN-Promise