共计 4581 个字符,预计需要花费 12 分钟才能阅读完成。
Promise
Promise 就像这个词的外表意识一样,示意一种承诺、允诺,会在前面给出一个后果,胜利 或者 失败。当初曾经成为了支流的异步编程的操作形式,写进了规范外面。
状态
Promise 有且仅有三种状态:
- 待定(pending): 初始状态,既没有被兑现,也没有被回绝。
- 已兑现(fulfilled): 意味着操作胜利实现。
- 已回绝(rejected): 意味着操作失败。
其实我感觉这个模型还是挺简略,也很好了解。就如同,你跟你女朋友求婚,她跟你说她要考虑一下,今天能力给你答案,这就是承诺(promise)。同时,这也是一个期待的过程(pending),而后你就等,等到今天你女朋友给你回答,批准(fulfilled)或者回绝(rejected),如果批准就筹备结婚了,如果不批准就等下次再求婚,哈哈哈。
那状态要怎么扭转呢?
Promise 的构造函数须要传入一个函数 executor,这个函数须要两个入参别离是 resolve 和 rejected 两个函数,这两个办法次要是用来批改状态的。当咱们调用 resolve 函数的时候,Promise 的状态就变成 fulfilled。当咱们调用 reject 函数的时候,Promise 的状态就变成 reject。举个栗子:
new Promise((resolve, reject) => {console.log("开始求婚。")
console.log("。。。。。")
console.log("考虑一下。")
setTimeout(() => {if (isHandsome || isRich) {resolve('我批准!')
} else {reject("回绝:咱们八字不合")
}
}, 2000)
})
这里还要留神一点,如果一个 promise 曾经被兑现(fulfilled)或被回绝(rejected),那么咱们也能够说它处于 已敲定(settled)状态。
链式调用
这点跟 Rx 还是挺类似的,也是链式调用,这样的益处就是异步逻辑更加清晰,连贯,合乎直觉,防止了回调天堂。
咱们能够用 Promise.then()
,Promise.catch()
和 Promise.finally()
这些办法将进一步的操作与一个变为已敲定状态的 Promise 关联起来。这些办法还会返回一个新生成的 Promise 对象,这个对象能够被非强制性的用来做链式调用,就像这样:
const myPromise =
(new Promise(myExecutorFunc))
.then(handleFulfilledA,handleRejectedA)
.then(handleFulfilledB,handleRejectedB)
.then(handleFulfilledC,handleRejectedC);
// 或者,这样可能会更好...
const myPromise =
(new Promise(myExecutorFunc))
.then(handleFulfilledA)
.then(handleFulfilledB)
.then(handleFulfilledC)
.catch(handleRejectedAny);
咱们个别举荐应用第二种的写法,因为第一种写法如果在两头出错,在 .then
中解决完之后,会持续走下一个 .then
的 onfulfilled
函数,不太合乎直觉。当然,如果你有马上解决异样的需要也能够这样写。
当 .then()
中短少可能返回 Promise 对象的函数时,链式调用就间接持续进行下一环操作。因而,链式调用能够在最初一个 .catch()
之前把所有的 handleRejection
都省略掉。相似地,.catch()
其实只是没有给 handleFulfilled
预留参数地位的 .then()
而已。
还是一个 Promise
这里要特地留神一个点,Promise.then()
、Promise.catch()
、Promise.finally()
这些办法会返回一个新的 Promise,这一点在其余很多文章中会疏忽这个点。.then()
函数最初会返回一个 Promise,如果在 .then
的 onfulfilled
(也就是入参的第一个函数)中返回一个值,或者对象,这个 Promise 的状态就会确定(settled),接着会触发下个 .then()
中的 onfulfilled
,并将这个 值 或者 对象作为入参传入。
诶,等一下,如果在 .then()
中间接返回一个 Promise 呢?
这个时候就会间接替换返回的 Promise。就跟 RxJava
中的 flatMap
操作挺类似的。MDN 中有阐明:
链式调用中的 promise 们就像俄罗斯套娃一样,是嵌套起来的,但又像是一个栈,每个都必须从顶端被弹出。链式调用中的第一个 promise 是嵌套最深的一个,也将是第一个被弹出的。
(promise D, (promise C, (promise B, (promise A) ) ) )
当存在一个
nextValue
是 promise 时,就会呈现一种动静的替换成果。return
会导致一个 promise 被弹出,但这个nextValue
promise 则会被推入被弹出 promise 原来的地位。对于下面所示的嵌套场景,假如与 “promise B” 相干的.then()
返回了一个值为 “promise X” 的nextValue
。那么嵌套的后果看起来就会是这样:(promise D, (promise C, (promise X) ) )
观察者模式?
而且一个 Promise 可能会参加不止一次的嵌套,感觉也像是观察者的模式,并反对多个观察者。对于上面的代码,promiseA
向 ” 已敲定 ”(”settled”)状态的过渡会导致两个实例的 .then
都被调用。
const promiseA = new Promise(myExecutorFunc);
const promiseB = promiseA.then(handleFulfilled1, handleRejected1);
const promiseC = promiseA.then(handleFulfilled2, handleRejected2);
写点代码
讲了这么多,写点代码吧,就写一个,女朋友屡次回绝之后看到了你的诚意,最初批准的故事。手动狗头。
let count = 0
const propose = () => {return new Promise((resolve, reject) => {console.log("开始求婚。")
console.log("。。。。。")
console.log("考虑一下。")
setTimeout(() => {if (count < 3) {reject("回绝:咱们八字不合")
count++
} else {resolve('我批准!')
}
}, 2000)
})
}
const startPropose = () => {propose()
.then((result) => {console.log(result)
console.log("完结撒花")
console.log("、·、·、、·、·、·、")
},)
.catch((e) => {
{console.error(e)
console.log("被回绝了,下次再求婚。")
setTimeout(() => {startPropose()
}, 3000)
}
})
}
startPropose()
补充:
还有一些小细节。
- Promise 一旦创立,传入的 executor 会被立即执行。
- 一个“已敲定”(”settled”)状态的 Promise 也能够承受操作,也就是说,前面能够接 then 或者 catch 函数。
-
所有 Promise 都是异步的,即使是“已敲定”(“settled”)了的 Promise 也是如此。一个曾经处于 ” 已敲定 ”(”settled”)状态的 promise 中的操作只有 promise 链式调用的栈被清空了和一个事件循环过来了之后才会被执行。这种成果跟
setTimeout(action, 10)
特地类似。const promiseA = new Promise((resolutionFunc,rejectionFunc) => {resolutionFunc(777); }); // 这时,"promiseA" 曾经被敲定了。promiseA.then((val) => console.log("asynchronous logging has val:",val) ); console.log("immediate logging"); // produces output in this order: // immediate logging // asynchronous logging has val: 777
-
其余一些静态方法
-
Promise.all(iterable)
这个办法返回一个新的 promise 对象,该 promise 对象在 iterable 参数对象里所有的 promise 对象都胜利的时候才会触发胜利,一旦有任何一个 iterable 外面的 promise 对象失败则立刻触发该 promise 对象的失败。这个新的 promise 对象在触发胜利状态当前,会把一个蕴含 iterable 里所有 promise 返回值的数组作为胜利回调的返回值,程序跟 iterable 的程序保持一致;如果这个新的 promise 对象触发了失败状态,它会把 iterable 里第一个触发失败的 promise 对象的错误信息作为它的失败错误信息。Promise.all 办法常被用于解决多个 promise 对象的状态汇合。(能够参考 jQuery.when 办法 — 译者注)
-
Promise.allSettled(iterable)
等到所有 promises 都已敲定(settled)(每个 promise 都已兑现(fulfilled)或已回绝(rejected))。返回一个 promise,该 promise 在所有 promise 实现后实现。并带有一个对象数组,每个对象对应每个 promise 的后果。
-
Promise.any(iterable)
接管一个 Promise 对象的汇合,当其中的一个 promise 胜利,就返回那个胜利的 promise 的值。
-
Promise.race(iterable)
当 iterable 参数里的任意一个子 promise 被胜利或失败后,父 promise 马上也会用子 promise 的胜利返回值或失败详情作为参数调用父 promise 绑定的相应句柄,并返回该 promise 对象。
-
Promise.reject(reason)
返回一个状态为失败的 Promise 对象,并将给定的失败信息传递给对应的解决办法
-
Promise.resolve(value)
返回一个状态由给定 value 决定的 Promise 对象。如果该值是 thenable(即,带有 then 办法的对象),返回的 Promise 对象的最终状态由 then 办法执行决定;否则的话(该 value 为空,根本类型或者不带 then 办法的对象), 返回的 Promise 对象状态为 fulfilled,并且将该 value 传递给对应的 then 办法。通常而言,如果您不晓得一个值是否是 Promise 对象,应用 Promise.resolve(value) 来返回一个 Promise 对象, 这样就能将该 value 以 Promise 对象模式应用。
-
参考:MDN(https://developer.mozilla.org…