摘要:處理和前一個(gè)回調(diào)函數(shù)運(yùn)行時(shí)發(fā)生的錯(cuò)誤發(fā)生錯(cuò)誤對(duì)象的錯(cuò)誤具有冒泡性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。
0 前言
我一直以為我對(duì)Promise比較了解,相關(guān)的方法已經(jīng)非常熟悉了,直到我看到這篇文章,里面提出了這樣一個(gè)問題:
Q: 假定 doSomething() 和 doSomethingElse() 均返回 promises,下面的四種 promises 的區(qū)別是什么
//1 doSomething().then(function(){ return doSomethingElse(); }).then(finalHandler); //2 doSomething().then(function(){ doSomethingElse(); }).then(finalHandler); //3 doSomething().then(doSomethingElse()) .then(finalHandler); //4 doSomething().then(doSomethingElse) .then(finalHandler);
我當(dāng)時(shí)看了是吃驚的,因?yàn)槲蚁?,這都什么玩意兒!??!所以我把Promise的方法復(fù)習(xí)了一遍,并且仔細(xì)讀了上面提到的那篇文章,于是就有了這篇文章。
在前端開發(fā)的學(xué)習(xí)中,新工具層出不窮,理解當(dāng)前的基礎(chǔ)是要理解過去,然后了解將來。就異步調(diào)用而言,ES6中引入Promise簡(jiǎn)化異步操作,主要針對(duì)的問題就是回調(diào)函數(shù)的層層嵌套(金字塔問題),除了閱讀不方便之外,只能在當(dāng)前回調(diào)函數(shù)函數(shù)內(nèi)部處理異常,這個(gè)很難做。Promise通過then和catch方法實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,每一次調(diào)用都返回一個(gè)Promise對(duì)象,擺脫了回調(diào)函數(shù)層層嵌套的問題和異步代碼“非線性執(zhí)行”的問題;另外,所有回調(diào)函數(shù)的報(bào)錯(cuò)都可以通過Promise統(tǒng)一處理,catch可以捕獲先前調(diào)用中所有的異常(冒泡特性)。但是Promise僅僅是對(duì)回調(diào)做了簡(jiǎn)化處理,ES7中的async函數(shù)更厲害,結(jié)合Promise,完全不用回調(diào)函數(shù),以近似同步的寫法實(shí)現(xiàn)異步操作,所需要的僅僅是一個(gè)async和await關(guān)鍵字而已。本文僅介紹Promise對(duì)象,以及ES6中Promise對(duì)象具有的一些操作方法。
1 簡(jiǎn)單Promise對(duì)象ES6中原生實(shí)現(xiàn)了Promise對(duì)象,通過狀態(tài)傳遞異步操作的消息。Promise對(duì)象有三種狀態(tài):pending(進(jìn)行中)、resoleved(fulfilled,已完成)、rejected(已失敗),根據(jù)異步操作的結(jié)果決定處于何種狀態(tài)。一旦狀態(tài)改變就不可再變,狀態(tài)改變僅有兩種pending=>rejected、pending=>resolved。
優(yōu)點(diǎn):避免了層層嵌套的回調(diào)函數(shù),并提供了統(tǒng)一的接口,使異步操作更加容易。
缺點(diǎn):無法取消Promise;如果不設(shè)置回調(diào)函數(shù),內(nèi)部錯(cuò)誤無法反映到外部。
Promise構(gòu)造函數(shù)接收兩個(gè)參數(shù):resolve和reject,這是兩個(gè)由JavaScript引擎自動(dòng)提供的函數(shù),不用自己部署。resolve函數(shù)在異步操作成功時(shí)調(diào)用,作用是將Promise對(duì)象的狀態(tài)由pending變?yōu)閞esolved,并將異步操作的結(jié)果傳遞出去。reject函數(shù)在異步操作失敗時(shí)調(diào)用,作用是將Promise對(duì)象的狀態(tài)由pending變?yōu)閞eject,將異步操作報(bào)錯(cuò)傳遞出去。
then方法可以接受兩個(gè)回調(diào)函數(shù)作為參數(shù),第一個(gè)是Pormise對(duì)象的狀態(tài)變?yōu)閞esolved時(shí)調(diào)用,另一個(gè)是當(dāng)Promise對(duì)象的狀態(tài)變?yōu)閞ejected時(shí)調(diào)用,這兩個(gè)回調(diào)函數(shù)都接受Promise對(duì)象實(shí)例創(chuàng)建過程中resolve函數(shù)和reject函數(shù)傳出的值作為參數(shù)。第二個(gè)參數(shù)可選,事實(shí)上一般通過Promise.prototype.catch()調(diào)用發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù),通過then調(diào)用異步操作成功時(shí)的回調(diào)函數(shù)。
實(shí)例1:
//返回Promise對(duì)象,setTimeout中傳遞的resolve參數(shù)為’done’ function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms, "done"); }); } timeout(100).then((value) => { console.log(value); }); //done
實(shí)例2:Promise執(zhí)行流
//創(chuàng)建Promise實(shí)例 let promise = new Promise(function(resolve, reject) { console.log("Promise");//立即執(zhí)行 resolve(); }); //resolved狀態(tài)調(diào)用在當(dāng)前腳本所有同步任務(wù)執(zhí)行完才會(huì)執(zhí)行 promise.then(function() { console.log("Resolved."); }); //立即執(zhí)行 console.log("Hi!");
對(duì)以上代碼,Promise新建后立即執(zhí)行,所以首先輸出的是“Promise”。然后,then方法指定的回調(diào)函數(shù),將在當(dāng)前腳本所有同步任務(wù)執(zhí)行完才會(huì)執(zhí)行,所以“Resolved”最后輸出。
reject函數(shù)在異步操作失敗時(shí)調(diào)用,因此參數(shù)常常是一個(gè)錯(cuò)誤對(duì)象(Error Object);resolve函數(shù)在操作成功時(shí)調(diào)用,參數(shù)常常是正常值或者另一個(gè)Promise實(shí)例,這表征了異步嵌套,異步嵌套的外層Promise要等待內(nèi)層Promise的狀態(tài)決定下一步狀態(tài)。
var p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error("fail")), 3000) }) var p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2.then(result => console.log(result)) //p1 is rejected, p2 is the same as p1 .catch(error => console.log(error)) // Error: fail
由于p2的resolve方法將p1作為參數(shù),p1的狀態(tài)決定了p2的狀態(tài),如果p1的狀態(tài)是pending,p2的回調(diào)函數(shù)會(huì)等待p1的狀態(tài)改變;如果p1的狀態(tài)是resolved或rejected,p2的回調(diào)函數(shù)立即執(zhí)行。p2的狀態(tài)在1秒之后改變,resolve方法返回的是p1。此時(shí),由于p2返回的是另一個(gè)Promise,所以后面的then語句都變成針對(duì)后者(p1)。又過了2秒,p1變?yōu)閞ejected,導(dǎo)致觸發(fā)catch方法指定的回調(diào)函數(shù)。
1.2 Promise.prototype.then()then方法為Promise實(shí)例添加狀態(tài)改變時(shí)的回調(diào)函數(shù),返回一個(gè)新的Promise實(shí)例,可以采用鏈?zhǔn)綄懛?,前一個(gè)then方法的返回值作為后一個(gè)then方法的參數(shù):
getJSON("/posts.json").then(function(json) { // json comes from “/posts.json” return json.post; }).then(function(post) { //post comes from json.post // ... });
如果第一個(gè)then方法內(nèi)的回調(diào)函數(shù)返回一個(gè)Promise對(duì)象,后續(xù)的then方法會(huì)根據(jù)這個(gè)新的Promise對(duì)象的狀態(tài)執(zhí)行回調(diào)函數(shù)。
getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function funcA(comments) { console.log("Resolved: ", comments); }, function funcB(err){ console.log("Rejected: ", err); });
第一個(gè)then方法指定的回調(diào)函數(shù),返回的是另一個(gè)Promise對(duì)象。這時(shí),第二個(gè)then方法指定的回調(diào)函數(shù),就會(huì)等待這個(gè)新的Promise對(duì)象狀態(tài)發(fā)生變化。如果變?yōu)镽esolved,就調(diào)用funcA,如果狀態(tài)變?yōu)镽ejected,就調(diào)用funcB。回調(diào)函數(shù)一般是匿名函數(shù),上述僅僅是為了便于理解寫成命名函數(shù)。
1.3 Promise.prototype.catch()catch(rejection)方法是then(null,rejection)的別稱,僅僅當(dāng)發(fā)生錯(cuò)誤時(shí)執(zhí)行,catch的存在是將錯(cuò)誤回調(diào)函數(shù)從then()方法中剝離出來。
getJSON("/posts.json").then(function(posts) { // ... }).catch(function(error) { // 處理 getJSON 和 前一個(gè)回調(diào)函數(shù)運(yùn)行時(shí)發(fā)生的錯(cuò)誤 console.log("發(fā)生錯(cuò)誤!", error); });
Promise 對(duì)象的錯(cuò)誤具有“冒泡”性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。也就是說,錯(cuò)誤總是會(huì)被下一個(gè)catch語句捕獲。
getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function(comments) { // some code }).catch(function(error) { // 處理前面三個(gè)Promise產(chǎn)生的錯(cuò)誤 });
catch方法返回的還是一個(gè) Promise 對(duì)象,因此后面還可以接著調(diào)用then方法。要是后續(xù)then方法里面報(bào)錯(cuò),就與前面的catch無關(guān)了。如果最后一個(gè)catch方法內(nèi)部拋出錯(cuò)誤,是無法捕獲的。為了避免潛在錯(cuò)誤,最好是在最后用一個(gè)catch方法兜底。
1.4 Promise.all():偏重狀態(tài)改變的邏輯與關(guān)系用于將多個(gè)Promise實(shí)例包裝成一個(gè)新的Promise實(shí)例。如果內(nèi)部參數(shù)不是Promise實(shí)例,就調(diào)用Promise.resolve()將參數(shù)轉(zhuǎn)換為Promise實(shí)例。
var p = Promise.all([p1, p2, p3]);//接受一個(gè)Promise數(shù)組作為參數(shù)
p的狀態(tài)由p1、p2、p3決定,呈現(xiàn)&關(guān)系,fulfilled對(duì)應(yīng)1,rejected對(duì)應(yīng)0,分成兩種情況:
(1)只有p1、p2、p3的狀態(tài)都變成fulfilled,p的狀態(tài)才會(huì)變成fulfilled,此時(shí)p1、p2、p3的返回值組成一個(gè)數(shù)組,傳遞給p的回調(diào)函數(shù)。
(2)只要p1、p2、p3之中有一個(gè)被rejected,p的狀態(tài)就變成rejected,此時(shí)第一個(gè)被reject的實(shí)例的返回值(rejected的順序有沒有類似與操作的順序?),會(huì)傳遞給p的回調(diào)函數(shù)。
const databasePromise = connectDatabase(); const booksPromise = databasePromise .then(findAllBooks); const userPromise = databasePromise .then(getCurrentUser); Promise.all([ booksPromise, userPromise ]) .then(([books, user]) => pickTopRecommentations(books, user));
上面代碼中,booksPromise和userPromise是兩個(gè)并行執(zhí)行的異步操作,只有等到它們的結(jié)果都返回了,才會(huì)觸發(fā)pickTopRecommentations這個(gè)回調(diào)函數(shù)。
1.5 Promise.race():偏重狀態(tài)改變的時(shí)間順序Promise.race()和Promise.all()同樣是將多個(gè)Promise實(shí)例包裝成一個(gè)新的Promise實(shí)例,但是只要實(shí)例數(shù)組中有一個(gè)實(shí)例率先改變狀態(tài),p的狀態(tài)就跟著改變。那個(gè)率先改變的 Promise 實(shí)例的返回值,就傳遞給新實(shí)例的回調(diào)函數(shù)。
var p = Promise.race([p1, p2, p3]);
Promise.race方法的參數(shù)與Promise.all方法一樣,如果不是 Promise 實(shí)例,就會(huì)先調(diào)用下面講到的Promise.resolve方法,將參數(shù)轉(zhuǎn)為 Promise 實(shí)例,再進(jìn)一步處理。
const p = Promise.race([ fetch("/resource-that-may-take-a-while"), new Promise(function (resolve, reject) { setTimeout(() => reject(new Error("request timeout")), 5000) }) ]); p.then(response => console.log(response)); p.catch(error => console.log(error));
上面代碼中,如果5秒之內(nèi)fetch方法無法返回結(jié)果,變量p的狀態(tài)就會(huì)變?yōu)閞ejected,從而觸發(fā)catch方法指定的回調(diào)函數(shù)。
1.6 Promise.resolve()將現(xiàn)有對(duì)象轉(zhuǎn)化為Promise對(duì)象,根據(jù)參數(shù)不同有不同結(jié)果:
(1) 參數(shù)是一個(gè)Promise實(shí)例,Promise.resolve()將原對(duì)象返回;
(2) 參數(shù)是具有then方法的對(duì)象,Promise.resolve方法會(huì)將這個(gè)對(duì)象轉(zhuǎn)為Promise對(duì)象,然后就立即執(zhí)行thenable對(duì)象的then方法。
let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 });
thenable對(duì)象的then方法執(zhí)行后,對(duì)象p1的狀態(tài)就變?yōu)閞esolved,從而立即執(zhí)行最后那個(gè)then方法指定的回調(diào)函數(shù),輸出42。
(3) 如果參數(shù)是一個(gè)原始值(基本類型值),或者是一個(gè)不具有then方法的對(duì)象,則Promise.resolve方法返回一個(gè)新的Promise對(duì)象,狀態(tài)為Resolved。
var p = Promise.resolve("Hello"); p.then(function (s){ console.log(s) }); // Hello
上面代碼生成一個(gè)新的Promise對(duì)象的實(shí)例p。由于字符串Hello不屬于異步操作(判斷方法是它不是具有then方法的對(duì)象),返回Promise實(shí)例的狀態(tài)從一生成就是Resolved,所以回調(diào)函數(shù)會(huì)立即執(zhí)行。Promise.resolve方法的參數(shù),會(huì)同時(shí)傳給回調(diào)函數(shù)。
(4) Promise.resolve方法允許調(diào)用時(shí)不帶參數(shù),直接返回一個(gè)Resolved狀態(tài)的Promise對(duì)象。所以,如果希望得到一個(gè)Promise對(duì)象,比較方便的方法就是直接調(diào)用Promise.resolve方法。
var p = Promise.resolve(); p.then(function () { // ... });
立即resolve的Promise對(duì)象,是在本輪“事件循環(huán)”(event loop)的結(jié)束時(shí),而不是在下一輪“事件循環(huán)”的開始時(shí),這個(gè)很好理解,通過resolve產(chǎn)生的Promise對(duì)象然后調(diào)用then函數(shù)和先產(chǎn)生Promise對(duì)象,對(duì)象轉(zhuǎn)換成resolved后再執(zhí)行then函數(shù)是一樣的,都是在本輪事件輪詢的末尾執(zhí)行。
//下一輪事件輪詢開始 setTimeout(function () { console.log("three"); }, 0); //本輪事件輪詢末尾 Promise.resolve().then(function () { console.log("two"); }); //立即執(zhí)行 console.log("one"); // one two three
上面代碼中,setTimeout(fn, 0)在下一輪“事件循環(huán)”開始時(shí)執(zhí)行,Promise.resolve()在本輪“事件循環(huán)”結(jié)束時(shí)執(zhí)行,console.log(’one‘)則是立即執(zhí)行,因此最先輸出。
1.7 Promise.reject()Promise.reject()返回一個(gè)Promise實(shí)例,實(shí)例狀態(tài)為rejected。Promise.reject()方法的參數(shù),會(huì)原封不動(dòng)地作為reject的理由,變成后續(xù)方法的參數(shù)。這一點(diǎn)與Promise.resolve方法不一致。
實(shí)例1:
var p = Promise.reject("出錯(cuò)了"); // 等同于var p = new Promise((resolve, reject) => reject("出錯(cuò)了")) //參數(shù)就是’出錯(cuò)了’ p.then(null, function (s) { console.log(s) });// 出錯(cuò)了
實(shí)例2:
const thenable = { then(resolve, reject) { reject("出錯(cuò)了"); } }; //參數(shù)就是thenable Promise.reject(thenable) .catch(e => { console.log(e === thenable) }) // true
上面代碼中,Promise.reject方法的參數(shù)是一個(gè)thenable對(duì)象,執(zhí)行以后,后面catch方法的參數(shù)不是reject拋出的“出錯(cuò)了”這個(gè)字符串,而是thenable對(duì)象。
2 加深Promise理解Promises 給予我們的就是在我們使用異步Callback時(shí)丟失的最重要的語言基石: return, throw 以及堆棧。但是想要 promises 能夠提供這些便利給你的前提是你知道如何正確的使用它們。
2.1 使用Promise.resolve()創(chuàng)建Promise對(duì)象任何有可能 throw 同步異常的代碼都是一個(gè)后續(xù)會(huì)導(dǎo)致幾乎無法調(diào)試異常的潛在因素。但是如果你將所有代碼都使用Promise.resolve() 封裝,那么你總是可以在之后使用 catch() 來捕獲它。因此方法2要優(yōu)于方法1。
方法1:
new Promise(function(resolve,reject){ resolve(someSynchronousValue); }).then(/*-------------*/);
方法2:
function somePromiseAPI() { return Promise.resolve().then(function(){ doSomethinThatMayThrow(); return ‘foo’; }).then(/*------------*/); }2.2 catch() 與 then(null, ...) 根據(jù)使用場(chǎng)景并非完全等價(jià)
以下代碼等價(jià):
somePromise().catch(function (err)){ //handle error }); ////////////////////////////////////// somePromise().then(null, function(err)) { //handle error }
但是以下代碼不等價(jià):
somePromise().then(function(){ return someOtherPromise(); }).catch(function(err){ //error }); /////////////////////////////// somePromise().then(function(){ return someOtherPromise(); },function(err){ //error });
因此,當(dāng)你使用 then(resolveHandler, rejectHandler) 這種形式時(shí),rejectHandler 并不會(huì)捕獲由 resolveHandler 引發(fā)的異常。最好不使用then()的第二個(gè)參數(shù),而是總是使用catch(),唯一例外是寫一些異步的Mocha測(cè)試時(shí),使用then()的第二個(gè)參數(shù),希望拋出用例的異常。
2.3 promises vs promises factories當(dāng)我們希望執(zhí)行一個(gè)個(gè)的執(zhí)行一個(gè) promises 序列,即類似 Promise.all() 但是并非并行的執(zhí)行所有 promises。你可能天真的寫下這樣的代碼:
function executeSequentially(promise){ var result = Promise.resolve(); promises.forEach(function (promise)){ result = result.then(promise); }); return result; }
不幸的是,這份代碼不會(huì)按照你的期望去執(zhí)行,你傳入 executeSequentially() 的 promises 依然會(huì)并行執(zhí)行。其根源在于你所希望的,實(shí)際上根本不是去執(zhí)行一個(gè) promises 序列。依照 promises 規(guī)范,一旦一個(gè) promise 被創(chuàng)建,它就被執(zhí)行了。因此你實(shí)際上需要的是一個(gè) promise factories 數(shù)組。
我知道你在想什么:“這是哪個(gè)見鬼的 Java 程序猿,他為啥在說 factories?” 。實(shí)際上,一個(gè) promises factory 是十分簡(jiǎn)單的,它僅僅是一個(gè)可以返回 promise 的函數(shù):
function executeSequentially(promiseFactories){ var result = Promise.resolve(); promiseFactories,forEach(function (promiseFactory){ result = result.then(promiseFactory) }); return result; } function promiseFactory(){ return somethingThatCreatesAPromise(); }
為何這樣就可以了?這是因?yàn)橐粋€(gè) promise factory 在被執(zhí)行之前并不會(huì)創(chuàng)建 promise。它就像一個(gè) then 函數(shù)一樣,而實(shí)際上,它們就是完全一樣的東西。如果你查看上面的 executeSequentially() 函數(shù),然后想象 myPromiseFactory 被包裹在 result.then(...) 之中,也許你腦中的小燈泡就會(huì)亮起。在此時(shí)此刻,對(duì)于 promise 你就算是悟道了。
2.4 promises 穿透Promise.resolve(‘foo’).then(Promise.resolve(‘bar’)).then(function(result){ console.log(result); });
執(zhí)行結(jié)果并非是bar,而是foo,這是因?yàn)楫?dāng)then()接受非函數(shù)的參數(shù)時(shí),會(huì)解釋為then(null),這就導(dǎo)致前一個(gè)Promise的結(jié)果穿透到下面一個(gè)Promise。正確的寫法是在then()方法內(nèi)部包含函數(shù):
Promise.resolve(‘foo’).then(function(){ return Promise.resolve(‘bar’); }).then(function(result){ console.log(result); });2.5 Promise.all()
Promise.all()以一個(gè)Promise數(shù)組作為輸入,返回一個(gè)新的Promise,特點(diǎn)在于它會(huì)并行執(zhí)行數(shù)組中的每個(gè)Promise,并且每個(gè)Promise都返回后才返回結(jié)果數(shù)組,這就數(shù)組的異步版map/forEach方法。但是如果需要返回兩個(gè)不相關(guān)的結(jié)果,使用Promise.all()可以產(chǎn)生兩個(gè)不相關(guān)的數(shù)組結(jié)果;但是如果后一結(jié)果要依靠前一個(gè)結(jié)果產(chǎn)生,此時(shí)在Promise里使用嵌套也就可以的:
getUserByName(‘bill’).then(function(user){ return getUserAccountById(user.id); }).then(function (userAccount){ /*-----------------*/ });
或者在內(nèi)部使用嵌套:
getUserByName(‘bill’).then(function(user){ return getUserAccountById(user.id).then(function(userAccount){ /*--------------------*/ }); });
忘記使用catch:沒人可以保證不出錯(cuò),所以還是在最后加一個(gè)catch吧!
Q: 假定 doSomething() 和 doSomethingElse() 均返回 promises,下面的四種 promises 的區(qū)別是什么
//1 doSomething().then(function(){ return doSomethingElse(); }).then(finalHandler); // doSomething()返回一個(gè)Promise實(shí)例,但是后續(xù)的then方法里是一個(gè)匿名函數(shù),該函數(shù)產(chǎn)生一個(gè)新的Promise實(shí)例并返回這個(gè)實(shí)例,因此finalHandler的參數(shù)就是這個(gè)實(shí)例的resolve返回值。 doSomething /-----------------/ doSomethingElse(undefined) /----------------------------------/ finalHandler(reulstOfDoSomethingElse) /--------------------------------------------------/ //2 doSomething().then(function(){ doSomethingElse(); }).then(finalHandler); //doSomething()返回一個(gè)Promise實(shí)例,但是后續(xù)的then方法里是一個(gè)匿名函數(shù),該函數(shù)產(chǎn)生一個(gè)新的Promise實(shí)例,但是由于這個(gè)函數(shù)沒有返回值,因此finalHandler函數(shù)沒有參數(shù)。 doSomething /-----------------/ doSomethingElse(undefined) /----------------------------------/ finalHandler(undefined) /----------------------------------/ //3 doSomething().then(doSomethingElse()) .then(finalHandler); // doSomething()返回一個(gè)Promise實(shí)例,但是后續(xù)的doSomethingElse()是一個(gè)立即執(zhí)行函數(shù),不接受上一Promise實(shí)例的resolve參數(shù),所以參數(shù)是undefined,這時(shí)doSomething()的Promise穿透到finalHandler,finalHandler的參數(shù)就是該P(yáng)romise的resolve參數(shù)。 doSomething /-----------------/ doSomethingElse(undefined) /----------------------------------/ finalHandler(reulstOfDoSomething) /--------------------------------------------------/ //4 doSomething().then(doSomethingElse) .then(finalHandler); //doSomething()返回一個(gè)Promise實(shí)例,隨后調(diào)用doSomethingElse以上一實(shí)例的resolve參數(shù)產(chǎn)生第二個(gè)Promise,最后是finalHandler以上一實(shí)例的resolve參數(shù)產(chǎn)生第三個(gè)Promise。 doSomething /-----------------/ doSomethingElse(resultOfDoSomething) /----------------------------------/ finalHandler(reulstOfDoSomethingElse) /--------------------------------------------------/
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/81867.html
摘要:實(shí)際上是格林威治標(biāo)準(zhǔn)時(shí)間的同義詞默認(rèn)情況下,中的幾乎每個(gè)日期方法除了一個(gè)都是本地時(shí)間。如果你住在格林威治標(biāo)準(zhǔn)時(shí)間晚的的地區(qū),你會(huì)得到一個(gè)日期是月日。需要知道對(duì)象日期方法。 為了保證的可讀性,本文采用意譯而非直譯。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! JS中的 Date 很奇怪。當(dāng)我們需要處理日期和時(shí)間的時(shí)候比較麻煩,經(jīng)常借助像date-fns和 Mom...
摘要:是流行的框架之一,在年及以后將會(huì)更加流行。于年首次發(fā)布,多年來廣受歡迎。下面是另一個(gè)名為的高階函數(shù)示例,該函數(shù)接受另外兩個(gè)函數(shù),分別是和。將所有較小的函數(shù)組合成更大的函數(shù),最終,得到一個(gè)應(yīng)用程序,這稱為組合。 React是流行的javascript框架之一,在2019年及以后將會(huì)更加流行。React于2013年首次發(fā)布,多年來廣受歡迎。它是一個(gè)聲明性的、基于組件的、用于構(gòu)建用戶界面的高...
摘要:應(yīng)屆生零基礎(chǔ)可以學(xué)習(xí)軟件測(cè)試嗎俗話說,人怕入錯(cuò)行。霸哥這里分別從入行難度入行方式行業(yè)前景薪資待遇四個(gè)方面來分析一下。目前市場(chǎng)上的測(cè)試人員,一部分是企業(yè)自己培養(yǎng)的,另一部分是來自培訓(xùn)機(jī)構(gòu)。軟件測(cè)試的行業(yè)門檻低,市場(chǎng)需求量大。 ...
摘要:然而在最近的面試中通過學(xué)習(xí)和思考,找到了前進(jìn)的方向,也得到一些大公司的錄用機(jī)會(huì)。算是從初級(jí)前端畢業(yè),進(jìn)階了吧。在這里先寫個(gè)目錄。趕時(shí)間的同學(xué)可以按照我的目錄先自行準(zhǔn)備提升,希望推薦文章和交流。 背景 之前我分享了文章大廠前端面試考什么?,你們一定很想看答案吧?說實(shí)話,答案我是有,在準(zhǔn)備面試的時(shí)候會(huì)時(shí)不時(shí)翻看,但內(nèi)容比較多,比較凌亂,不能指望我在一篇文章中寫完。 我是從非計(jì)算機(jī)專業(yè)自學(xué)前...
摘要:并發(fā)線程測(cè)試循環(huán)新建線程類,并在線程體內(nèi)塞入單個(gè)的測(cè)試用例,以及全局的計(jì)數(shù)類。為了讓性能測(cè)試更充分,我編寫了不同的計(jì)算過程,并使用隨機(jī)函數(shù)隨機(jī)獲取并塞入線程執(zhí)行。 ...
閱讀 2493·2021-10-14 09:42
閱讀 1146·2021-09-22 15:09
閱讀 3549·2021-09-09 09:33
閱讀 3030·2021-09-07 09:59
閱讀 3643·2021-09-03 10:34
閱讀 3545·2021-07-26 22:01
閱讀 2827·2019-08-30 13:06
閱讀 1210·2019-08-30 10:48