[稳扎稳打系列] - [Javascript] 什么你说不太懂 Promise ,没关係我教你

前言

不知道你有没有注意过早上的起床顺序呢,像是作者本来起床的顺序是:

拉开被子刷牙喝咖啡上班

有注意到吗这些事情都是一个一个执行,相信应该没有人可以一边刷牙一边喝咖啡吧!
人脑是如此,电脑也不意外同时间一个执行绪能做的也就是一件事情,

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 就会出现(误) Here's 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 的结果。

Chain

需要特别注意: 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}

status 代表此 Promise 是否成功 fulfilled / rejectedvalue 当 Promise 成功时,此数值为 resolve 夹带资讯reason 当 Promise 失败时,此数值为 reject 夹带资讯
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


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章