成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

初探Promise

wall2flower / 529人閱讀

摘要:實(shí)例生成以后,可以用方法指定狀態(tài)和狀態(tài)的回調(diào)函數(shù)。語(yǔ)法該方法是的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。

一 前言

本文主要對(duì)ES6的Promise進(jìn)行一些入門(mén)級(jí)的介紹。要想學(xué)習(xí)一個(gè)知識(shí)點(diǎn),肯定是從三個(gè)方面出發(fā),what、why、how。下面就跟著我一步步學(xué)習(xí)吧~

二 什么是Promise

首先是what。那么什么是Promise呢?
以下是MDN對(duì)Promise的定義

The Promise object is used for asynchronous computations. A Promise represents a single asynchronous operation that hasn"t completed yet, but is expected in the future.

譯文:Promise對(duì)象用于異步操作,它表示一個(gè)尚未完成且預(yù)計(jì)在未來(lái)完成的異步操作。

那么什么是異步操作?在學(xué)習(xí)promise之前需要把這個(gè)概念搞明白,下面將抽離一章專門(mén)介紹。

2.1 同步與異步

我們知道,JavaScript的執(zhí)行環(huán)境是「單線程」。
所謂單線程,是指JS引擎中負(fù)責(zé)解釋和執(zhí)行JavaScript代碼的線程只有一個(gè),也就是一次只能完成一項(xiàng)任務(wù),這個(gè)任務(wù)執(zhí)行完后才能執(zhí)行下一個(gè),它會(huì)「阻塞」其他任務(wù)。這個(gè)任務(wù)可稱為主線程。
但實(shí)際上還有其他線程,如事件觸發(fā)線程、ajax請(qǐng)求線程等。

這也就引發(fā)了同步和異步的問(wèn)題。

2.1.1 同步

同步模式,即上述所說(shuō)的單線程模式,一次只能執(zhí)行一個(gè)任務(wù),函數(shù)調(diào)用后需等到函數(shù)執(zhí)行結(jié)束,返回執(zhí)行的結(jié)果,才能進(jìn)行下一個(gè)任務(wù)。如果這個(gè)任務(wù)執(zhí)行的時(shí)間較長(zhǎng),就會(huì)導(dǎo)致「線程阻塞」。

/* 例2.1 */
var x = true;
while(x);
console.log("don"t carry out");    //不會(huì)執(zhí)行

上面的例子即同步模式,其中的while是一個(gè)死循環(huán),它會(huì)阻塞進(jìn)程,因此第三句console不會(huì)執(zhí)行。
同步模式比較簡(jiǎn)單,也較容易編寫(xiě)。但問(wèn)題也顯而易見(jiàn),如果請(qǐng)求的時(shí)間較長(zhǎng),而阻塞了后面代碼的執(zhí)行,體驗(yàn)是很不好的。因此對(duì)于一些耗時(shí)的操作,異步模式則是更好的選擇。

2.1.2 異步

下面就來(lái)看看異步模式。
異步模式,即與同步模式相反,可以一起執(zhí)行多個(gè)任務(wù),函數(shù)調(diào)用后不會(huì)立即返回執(zhí)行的結(jié)果,如果任務(wù)A需要等待,可先執(zhí)行任務(wù)B,等到任務(wù)A結(jié)果返回后再繼續(xù)回調(diào)。
最常見(jiàn)的異步模式就數(shù)定時(shí)器了,我們來(lái)看看以下的例子。

/* 例2.2 */
setTimeout(function() {
    console.log("taskA, asynchronous");
}, 0);
console.log("taskB, synchronize");
//while(true);

-------ouput-------
taskB, synchronize
taskA, asynchronous

我們可以看到,定時(shí)器延時(shí)的時(shí)間明明為0,但taskA還是晚于taskB執(zhí)行。這是為什么呢?由于定時(shí)器是異步的,異步任務(wù)會(huì)在當(dāng)前腳本的所有同步任務(wù)執(zhí)行完才會(huì)執(zhí)行。如果同步代碼中含有死循環(huán),即將上例的注釋去掉,那么這個(gè)異步任務(wù)就不會(huì)執(zhí)行,因?yàn)橥饺蝿?wù)阻塞了進(jìn)程。

2.1.3 回調(diào)函數(shù)

提起異步,就不得不談?wù)劵卣{(diào)函數(shù)了。上例中,setTimeout里的function便是回調(diào)函數(shù)??梢院?jiǎn)單理解為:(執(zhí)行完)回(來(lái))調(diào)(用)的函數(shù)。
以下是WikiPedia對(duì)于callback的定義。

In computer programming, a callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time.

可以看出,回調(diào)函數(shù)是一段可執(zhí)行的代碼段,它以「參數(shù)」的形式傳遞給其他代碼,在其合適的時(shí)間執(zhí)行這段(回調(diào)函數(shù))的代碼。

WikiPedia同時(shí)提到

The invocation may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback.

也就是說(shuō),回調(diào)函數(shù)不僅可以用于異步調(diào)用,一般同步的場(chǎng)景也可以用回調(diào)。在同步調(diào)用下,回調(diào)函數(shù)一般是最后執(zhí)行的。而異步調(diào)用下,可能一段時(shí)間后執(zhí)行或不執(zhí)行(未達(dá)到執(zhí)行的條件)。

/* 例2.3 */
/******************同步回調(diào)******************/
var fun1 = function(callback) {
    //do something
    console.log("before callback");
    (callback && typeof(callback) === "function") && callback();
    console.log("after callback");
}
var fun2 = function(param) {
    //do something
    var start = new Date();
    while((new Date() - start) < 3000) { //delay 3s
    }
    console.log("I"m callback");
}
fun1(fun2);

-------output--------
before callback
//after 3s
I’m callback
after callback

由于是同步回調(diào),會(huì)阻塞后面的代碼,如果fun2是個(gè)死循環(huán),后面的代碼就不執(zhí)行了。

上一小節(jié)中setTimeout就是常見(jiàn)的異步回調(diào),另外常見(jiàn)的異步回調(diào)即ajax請(qǐng)求。

/* 例2.4 */
/******************異步回調(diào)******************/
function request(url, param, successFun, errorFun) {
    $.ajax({
        type: "GET",
        url: url,
        param: param,
        async: true,    //默認(rèn)為true,即異步請(qǐng)求;false為同步請(qǐng)求
        success: successFun,
        error: errorFun
    });
}
request("test.html", "", function(data) {
    //請(qǐng)求成功后的回調(diào)函數(shù),通常是對(duì)請(qǐng)求回來(lái)的數(shù)據(jù)進(jìn)行處理
    console.log("請(qǐng)求成功啦, 這是返回的數(shù)據(jù):", data);
},function(error) {
    console.log("sorry, 請(qǐng)求失敗了, 這是失敗信息:", error);
});
2.2 為什么使用Promise

說(shuō)完了以上基本概念,我們就可以繼續(xù)學(xué)習(xí)Promise了。
上面提到,Promise對(duì)象是用于異步操作的。既然我們可以使用異步回調(diào)來(lái)進(jìn)行異步操作,為什么還要引入一個(gè)Promise新概念,還要花時(shí)間學(xué)習(xí)它呢?不要著急,下面就來(lái)談?wù)?b>Promise的過(guò)人之處。
我們先看看下面的demo,利用Promise改寫(xiě)例2.4的異步回調(diào)。

/* 例2.5 */
function sendRequest(url, param) {
    return new Promise(function (resolve, reject) {
        request(url, param, resolve, reject);
    });
}

sendRequest("test.html", "").then(function(data) {
    //異步操作成功后的回調(diào)
    console.log("請(qǐng)求成功啦, 這是返回的數(shù)據(jù):", data);
}, function(error) {
    //異步操作失敗后的回調(diào)
    console.log("sorry, 請(qǐng)求失敗了, 這是失敗信息:", error);
});

這么一看,并沒(méi)有什么區(qū)別,還比上面的異步回調(diào)復(fù)雜,得先新建Promise再定義其回調(diào)。其實(shí),Promise的真正強(qiáng)大之處在于它的多重鏈?zhǔn)秸{(diào)用,可以避免層層嵌套回調(diào)。如果我們?cè)诘谝淮蝍jax請(qǐng)求后,還要用它返回的結(jié)果再次請(qǐng)求呢?

/* 例2.6 */
request("test1.html", "", function(data1) {
    console.log("第一次請(qǐng)求成功, 這是返回的數(shù)據(jù):", data1);
    request("test2.html", data1, function (data2) {
        console.log("第二次請(qǐng)求成功, 這是返回的數(shù)據(jù):", data2);
        request("test3.html", data2, function (data3) {
            console.log("第三次請(qǐng)求成功, 這是返回的數(shù)據(jù):", data3);
            //request... 繼續(xù)請(qǐng)求
        }, function(error3) {
            console.log("第三次請(qǐng)求失敗, 這是失敗信息:", error3);
        });
    }, function(error2) {
        console.log("第二次請(qǐng)求失敗, 這是失敗信息:", error2);
    });
}, function(error1) {
    console.log("第一次請(qǐng)求失敗, 這是失敗信息:", error1);
});

以上出現(xiàn)了多層回調(diào)嵌套,有種暈頭轉(zhuǎn)向的感覺(jué)。這也就是我們常說(shuō)的厄運(yùn)回調(diào)金字塔(Pyramid of Doom),編程體驗(yàn)十分不好。而使用Promise,我們就可以利用then進(jìn)行「鏈?zhǔn)交卣{(diào)」,將異步操作以同步操作的流程表示出來(lái)。

/* 例2.7 */
sendRequest("test1.html", "").then(function(data1) {
    console.log("第一次請(qǐng)求成功, 這是返回的數(shù)據(jù):", data1);
    return sendRequest("test2.html", data1);
}).then(function(data2) {
    console.log("第二次請(qǐng)求成功, 這是返回的數(shù)據(jù):", data2);
    return sendRequest("test3.html", data2);
}).then(function(data3) {
    console.log("第三次請(qǐng)求成功, 這是返回的數(shù)據(jù):", data3);
}).catch(function(error) {
    //用catch捕捉前面的錯(cuò)誤
    console.log("sorry, 請(qǐng)求失敗了, 這是失敗信息:", error);
});

是不是明顯清晰很多?孰優(yōu)孰略也無(wú)需多說(shuō)了吧~下面就讓我們真正進(jìn)入Promise的學(xué)習(xí)。

三 Promise的基本用法 3.1 基本用法

上一小節(jié)我們認(rèn)識(shí)了promise長(zhǎng)什么樣,但對(duì)它用到的resolve、rejectthen、catch想必還不理解。下面我們一步步學(xué)習(xí)。

Promise對(duì)象代表一個(gè)未完成、但預(yù)計(jì)將來(lái)會(huì)完成的操作。
它有以下三種狀態(tài):

pending:初始值,不是fulfilled,也不是rejected

fulfilled:代表操作成功

rejected:代表操作失敗

Promise有兩種狀態(tài)改變的方式,既可以從pending轉(zhuǎn)變?yōu)?b>fulfilled,也可以從pending轉(zhuǎn)變?yōu)?b>rejected。一旦狀態(tài)改變,就「凝固」了,會(huì)一直保持這個(gè)狀態(tài),不會(huì)再發(fā)生變化。當(dāng)狀態(tài)發(fā)生變化,promise.then綁定的函數(shù)就會(huì)被調(diào)用。
注意:Promise一旦新建就會(huì)「立即執(zhí)行」,無(wú)法取消。這也是它的缺點(diǎn)之一。
下面就通過(guò)例子進(jìn)一步講解。

/* 例3.1 */
//構(gòu)建Promise
var promise = new Promise(function (resolve, reject) {
    if (/* 異步操作成功 */) {
        resolve(data);
    } else {
        /* 異步操作失敗 */
        reject(error);
    }
});

類(lèi)似構(gòu)建對(duì)象,我們使用new來(lái)構(gòu)建一個(gè)Promise。Promise接受一個(gè)「函數(shù)」作為參數(shù),該函數(shù)的兩個(gè)參數(shù)分別是resolvereject。這兩個(gè)函數(shù)就是就是「回調(diào)函數(shù)」,由JavaScript引擎提供。

resolve函數(shù)的作用:在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去;
reject函數(shù)的作用:在異步操作失敗時(shí)調(diào)用,并將異步操作報(bào)出的錯(cuò)誤,作為參數(shù)傳遞出去。

Promise實(shí)例生成以后,可以用then方法指定resolved狀態(tài)和reject狀態(tài)的回調(diào)函數(shù)。

/* 接例3.1 */
promise.then(onFulfilled, onRejected);

promise.then(function(data) {
  // do something when success
}, function(error) {
  // do something when failure
});

then方法會(huì)返回一個(gè)Promise。它有兩個(gè)參數(shù),分別為Promise從pending變?yōu)?b>fulfilled和rejected時(shí)的回調(diào)函數(shù)(第二個(gè)參數(shù)非必選)。這兩個(gè)函數(shù)都接受Promise對(duì)象傳出的值作為參數(shù)。
簡(jiǎn)單來(lái)說(shuō),then就是定義resolvereject函數(shù)的,其resolve參數(shù)相當(dāng)于:

function resolveFun(data) {
    //data為promise傳出的值
}

而新建Promise中的"resolve(data)",則相當(dāng)于執(zhí)行resolveFun函數(shù)。
Promise新建后就會(huì)立即執(zhí)行。而then方法中指定的回調(diào)函數(shù),將在當(dāng)前腳本所有同步任務(wù)執(zhí)行完才會(huì)執(zhí)行。如下例:

/* 例3.2 */
var promise = new Promise(function(resolve, reject) {
  console.log("before resolved");
  resolve();
  console.log("after resolved");
});

promise.then(function() {
  console.log("resolved");
});

console.log("outer");

-------output-------
before resolved
after resolved
outer
resolved

由于resolve指定的是異步操作成功后的回調(diào)函數(shù),它需要等所有同步代碼執(zhí)行后才會(huì)執(zhí)行,因此最后打印"resolved",這個(gè)和例2.2是一樣的道理。

3.2 基本API .then()
語(yǔ)法:Promise.prototype.then(onFulfilled, onRejected)

對(duì)promise添加onFulfilledonRejected回調(diào),并返回的是一個(gè)新的Promise實(shí)例(不是原來(lái)那個(gè)Promise實(shí)例),且返回值將作為參數(shù)傳入這個(gè)新Promise的resolve函數(shù)。

因此,我們可以使用鏈?zhǔn)綄?xiě)法,如上文的例2.7。由于前一個(gè)回調(diào)函數(shù),返回的還是一個(gè)Promise對(duì)象(即有異步操作),這時(shí)后一個(gè)回調(diào)函數(shù),就會(huì)等待該P(yáng)romise對(duì)象的狀態(tài)發(fā)生變化,才會(huì)被調(diào)用。

.catch()
語(yǔ)法:Promise.prototype.catch(onRejected)

該方法是.then(undefined, onRejected)的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。

/* 例3.3 */
promise.then(function(data) {
    console.log("success");
}).catch(function(error) {
    console.log("error", error);
});

/*******等同于*******/
promise.then(function(data) {
    console.log("success");
}).then(undefined, function(error) {
    console.log("error", error);
});
/* 例3.4 */
var promise = new Promise(function (resolve, reject) {
    throw new Error("test");
});
/*******等同于*******/
var promise = new Promise(function (resolve, reject) {
    reject(new Error("test"));
});

//用catch捕獲
promise.catch(function (error) {
    console.log(error);
});
-------output-------
Error: test

從上例可以看出,reject方法的作用,等同于拋錯(cuò)。

promise對(duì)象的錯(cuò)誤,會(huì)一直向后傳遞,直到被捕獲。即錯(cuò)誤總會(huì)被下一個(gè)catch所捕獲。then方法指定的回調(diào)函數(shù),若拋出錯(cuò)誤,也會(huì)被下一個(gè)catch捕獲。catch中也能拋錯(cuò),則需要后面的catch來(lái)捕獲。

/* 例3.5 */
sendRequest("test.html").then(function(data1) {
    //do something
}).then(function (data2) {
    //do something
}).catch(function (error) {
    //處理前面三個(gè)Promise產(chǎn)生的錯(cuò)誤
});

上文提到過(guò),promise狀態(tài)一旦改變就會(huì)凝固,不會(huì)再改變。因此promise一旦fulfilled了,再拋錯(cuò),也不會(huì)變?yōu)?b>rejected,就不會(huì)被catch了。

/* 例3.6 */
var promise = new Promise(function(resolve, reject) {
  resolve();
  throw "error";
});

promise.catch(function(e) {
   console.log(e);      //This is never called
});

如果沒(méi)有使用catch方法指定處理錯(cuò)誤的回調(diào)函數(shù),Promise對(duì)象拋出的錯(cuò)誤不會(huì)傳遞到外層代碼,即不會(huì)有任何反應(yīng)(Chrome會(huì)拋錯(cuò)),這是Promise的另一個(gè)缺點(diǎn)。

/* 例3.7 */
var promise = new Promise(function (resolve, reject) {
    resolve(x);
});
promise.then(function (data) {
    console.log(data);
});

如圖所示,只有Chrome會(huì)拋錯(cuò),且promise狀態(tài)變?yōu)?b>rejected,F(xiàn)irefox和Safari中錯(cuò)誤不會(huì)被捕獲,也不會(huì)傳遞到外層代碼,最后沒(méi)有任何輸出,promise狀態(tài)也變?yōu)?b>rejected。

.all()
語(yǔ)法:Promise.all(iterable)

該方法用于將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例。

var p = Promise.all([p1, p2, p3]);

Promise.all方法接受一個(gè)數(shù)組(或具有Iterator接口)作參數(shù),數(shù)組中的對(duì)象(p1、p2、p3)均為promise實(shí)例(如果不是一個(gè)promise,該項(xiàng)會(huì)被用Promise.resolve轉(zhuǎn)換為一個(gè)promise)。它的狀態(tài)由這三個(gè)promise實(shí)例決定。

當(dāng)p1, p2, p3狀態(tài)都變?yōu)?b>fulfilled,p的狀態(tài)才會(huì)變?yōu)?b>fulfilled,并將三個(gè)promise返回的結(jié)果,按參數(shù)的順序(而不是 resolved的順序)存入數(shù)組,傳給p的回調(diào)函數(shù),如例3.8。

當(dāng)p1, p2, p3其中之一狀態(tài)變?yōu)?b>rejected,p的狀態(tài)也會(huì)變?yōu)?b>rejected,并把第一個(gè)被reject的promise的返回值,傳給p的回調(diào)函數(shù),如例3.9。

/* 例3.8 */
var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 3000, "first");
});
var p2 = new Promise(function (resolve, reject) {
    resolve("second");
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, "third");
}); 

Promise.all([p1, p2, p3]).then(function(values) { 
  console.log(values); 
});

-------output-------
//約 3s 后
["first", "second", "third"] 
/* 例3.9 */
var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, "one"); 
}); 
var p2 = new Promise((resolve, reject) => { 
  setTimeout(reject, 2000, "two"); 
});
var p3 = new Promise((resolve, reject) => {
  reject("three");
});

Promise.all([p1, p2, p3]).then(function (value) {
    console.log("resolve", value);
}, function (error) {
    console.log("reject", error);    // => reject three
});

-------output-------
reject three

這多個(gè) promise 是同時(shí)開(kāi)始、并行執(zhí)行的,而不是順序執(zhí)行。從下面例子可以看出。如果一個(gè)個(gè)執(zhí)行,那至少需要 1+32+64+128

/* 例3.10 */
function timerPromisefy(delay) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
var startDate = Date.now();

Promise.all([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (values) {
    console.log(Date.now() - startDate + "ms");
    console.log(values);
});
-------output-------
133ms       //不一定,但大于128ms
[1,32,64,128]
.race()
語(yǔ)法:Promise.race(iterable)

該方法同樣是將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例。

var p = Promise.race([p1, p2, p3]);

Promise.race方法同樣接受一個(gè)數(shù)組(或具有Iterator接口)作參數(shù)。當(dāng)p1, p2, p3中有一個(gè)實(shí)例的狀態(tài)發(fā)生改變(變?yōu)?b>fulfilled或rejected),p的狀態(tài)就跟著改變。并把第一個(gè)改變狀態(tài)的promise的返回值,傳給p的回調(diào)函數(shù)。

/* 例3.11 */
var p1 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 500, "one"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "two"); 
});

Promise.race([p1, p2]).then(function(value) {
    console.log("resolve", value); 
}, function(error) {
    //not called
    console.log("reject", error); 
});
-------output-------
resolve two

var p3 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "three");
});
var p4 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 100, "four"); 
});

Promise.race([p3, p4]).then(function(value) {
    //not called
    console.log("resolve", value);              
}, function(error) {
    console.log("reject", error); 
});
-------output-------
reject four

在第一個(gè)promise對(duì)象變?yōu)閞esolve后,并不會(huì)取消其他promise對(duì)象的執(zhí)行,如下例

/* 例3.12 */
var fastPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log("fastPromise");
        resolve("resolve fastPromise");
    }, 100);
});
var slowPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log("slowPromise");
        resolve("resolve slowPromise");
    }, 1000);
});
// 第一個(gè)promise變?yōu)閞esolve后程序停止
Promise.race([fastPromise, slowPromise]).then(function (value) {
    console.log(value);    // => resolve fastPromise
});
-------output-------
fastPromise
resolve fastPromise
slowPromise     //仍會(huì)執(zhí)行
.resolve()

語(yǔ)法:

Promise.resolve(value);
Promise.resolve(promise);
Promise.resolve(thenable);

它可以看做new Promise()的快捷方式。

Promise.resolve("Success");

/*******等同于*******/
new Promise(function (resolve) {
    resolve("Success");
});

這段代碼會(huì)讓這個(gè)Promise對(duì)象立即進(jìn)入resolved狀態(tài),并將結(jié)果success傳遞給then指定的onFulfilled回調(diào)函數(shù)。由于Promise.resolve()也是返回Promise對(duì)象,因此可以用.then()處理其返回值。

/* 例3.13 */
Promise.resolve("success").then(function (value) {
    console.log(value);
});
-------output-------
Success
/* 例3.14 */
//Resolving an array
Promise.resolve([1,2,3]).then(function(value) {
  console.log(value[0]);    // => 1
});

//Resolving a Promise
var p1 = Promise.resolve("this is p1");
var p2 = Promise.resolve(p1);
p2.then(function (value) {
    console.log(value);     // => this is p1
});

Promise.resolve()的另一個(gè)作用就是將thenable對(duì)象(即帶有then方法的對(duì)象)轉(zhuǎn)換為promise對(duì)象。

/* 例3.15 */
var p1 = Promise.resolve({ 
    then: function (resolve, reject) { 
        resolve("this is an thenable object!");
    }
});
console.log(p1 instanceof Promise);     // => true

p1.then(function(value) {
    console.log(value);     // => this is an thenable object!
  }, function(e) {
    //not called
});

再看下面兩個(gè)例子,無(wú)論是在什么時(shí)候拋異常,只要promise狀態(tài)變成resolvedrejected,狀態(tài)不會(huì)再改變,這和新建promise是一樣的。

/* 例3.16 */
//在回調(diào)函數(shù)前拋異常
var p1 = { 
    then: function(resolve) {
      throw new Error("error");
      resolve("Resolved");
    }
};

var p2 = Promise.resolve(p1);
p2.then(function(value) {
    //not called
}, function(error) {
    console.log(error);       // => Error: error
});

//在回調(diào)函數(shù)后拋異常
var p3 = { 
    then: function(resolve) {
        resolve("Resolved");
        throw new Error("error");
    }
};

var p4 = Promise.resolve(p3);
p4.then(function(value) {
    console.log(value);     // => Resolved
}, function(error) {
    //not called
});
.reject()
語(yǔ)法:Promise.reject(reason)

這個(gè)方法和上述的Promise.resolve()類(lèi)似,它也是new Promise()的快捷方式。

Promise.reject(new Error("error"));

/*******等同于*******/
new Promise(function (resolve, reject) {
    reject(new Error("error"));
});

這段代碼會(huì)讓這個(gè)Promise對(duì)象立即進(jìn)入rejected狀態(tài),并將錯(cuò)誤對(duì)象傳遞給then指定的onRejected回調(diào)函數(shù)。

四 Promise常見(jiàn)問(wèn)題

經(jīng)過(guò)上一章的學(xué)習(xí),相信大家已經(jīng)學(xué)會(huì)使用Promise。
總結(jié)一下創(chuàng)建promise的流程:

使用new Promise(fn)或者它的快捷方式Promise.resolve()Promise.reject(),返回一個(gè)promise對(duì)象

fn中指定異步的處理
處理結(jié)果正常,調(diào)用resolve
處理結(jié)果錯(cuò)誤,調(diào)用reject

如果使用ES6的箭頭函數(shù),將會(huì)使寫(xiě)法更加簡(jiǎn)單清晰。

這一章節(jié),將會(huì)用例子的形式,以說(shuō)明promise使用過(guò)程中的注意點(diǎn)及容易犯的錯(cuò)誤。

情景1:reject 和 catch 的區(qū)別

promise.then(onFulfilled, onRejected)
onFulfilled中發(fā)生異常的話,在onRejected中是捕獲不到這個(gè)異常的。

promise.then(onFulfilled).catch(onRejected)
.then中產(chǎn)生的異常能在.catch中捕獲

一般情況,還是建議使用第二種,因?yàn)槟懿东@之前的所有異常。當(dāng)然了,第二種的.catch()也可以使用.then()表示,它們本質(zhì)上是沒(méi)有區(qū)別的,.catch === .then(null, onRejected)

情景2:如果在then中拋錯(cuò),而沒(méi)有對(duì)錯(cuò)誤進(jìn)行處理(即catch),那么會(huì)一直保持reject狀態(tài),直到catch了錯(cuò)誤

/* 例4.1 */
function taskA() {
    console.log(x);
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejected(error) {
    console.log("Catch Error: A or B", error);
}
function finalTask() {
    console.log("Final Task");
}
var promise = Promise.resolve();
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected)
    .then(finalTask);
    
-------output-------
Catch Error: A or B,ReferenceError: x is not defined
Final Task

根據(jù)例4.1的輸出結(jié)果及流程圖,可以看出,A拋錯(cuò)時(shí),會(huì)按照 taskA → onRejected → finalTask這個(gè)流程來(lái)處理。A拋錯(cuò)后,若沒(méi)有對(duì)它進(jìn)行處理,如例3.7,狀態(tài)就會(huì)維持rejected,taskB不會(huì)執(zhí)行,直到catch了錯(cuò)誤。

/* 例4.2 */
function taskA() {
    console.log(x);
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejectedA(error) {
    console.log("Catch Error: A", error);
}
function onRejectedB(error) {
    console.log("Catch Error: B", error);
}
function finalTask() {
    console.log("Final Task");
}
var promise = Promise.resolve();
promise
    .then(taskA)
    .catch(onRejectedA)
    .then(taskB)
    .catch(onRejectedB)
    .then(finalTask);
    
-------output-------
Catch Error: A ReferenceError: x is not defined
Task B
Final Task

將例4.2與4.1對(duì)比,在taskA后多了對(duì)A的處理,因此,A拋錯(cuò)時(shí),會(huì)按照A會(huì)按照 taskA → onRejectedA → taskB → finalTask這個(gè)流程來(lái)處理,此時(shí)taskB是正常執(zhí)行的。

情景3:每次調(diào)用then都會(huì)返回一個(gè)新創(chuàng)建的promise對(duì)象,而then內(nèi)部只是返回的數(shù)據(jù)

/* 例4.3 */
//方法1:對(duì)同一個(gè)promise對(duì)象同時(shí)調(diào)用 then 方法
var p1 = new Promise(function (resolve) {
    resolve(100);
});
p1.then(function (value) {
    return value * 2;
});
p1.then(function (value) {
    return value * 2;
});
p1.then(function (value) {
    console.log("finally: " + value);
});
-------output-------
finally: 100

//方法2:對(duì) then 進(jìn)行 promise chain 方式進(jìn)行調(diào)用
var p2 = new Promise(function (resolve) {
    resolve(100);
});
p2.then(function (value) {
    return value * 2;
}).then(function (value) {
    return value * 2;
}).then(function (value) {
    console.log("finally: " + value);
});
-------output-------
finally: 400

第一種方法中,then的調(diào)用幾乎是同時(shí)開(kāi)始執(zhí)行的,且傳給每個(gè)then的value都是100,這種方法應(yīng)當(dāng)避免。方法二才是正確的鏈?zhǔn)秸{(diào)用。
因此容易出現(xiàn)下面的錯(cuò)誤寫(xiě)法:

/* 例4.4 */
function badAsyncCall(data) {
    var promise = Promise.resolve(data);
    promise.then(function(value) {
        //do something
        return value + 1;
    });
    return promise;
}
badAsyncCall(10).then(function(value) {
   console.log(value);          //想要得到11,實(shí)際輸出10
});
-------output-------
10

正確的寫(xiě)法應(yīng)該是:

/* 改寫(xiě)例4.4 */
function goodAsyncCall(data) {
    var promise = Promise.resolve(data);
    return promise.then(function(value) {
        //do something
        return value + 1;
    });
}
goodAsyncCall(10).then(function(value) {
   console.log(value);
});
-------output-------
11

情景4:在異步回調(diào)中拋錯(cuò),不會(huì)被catch

// Errors thrown inside asynchronous functions will act like uncaught errors
var promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw "Uncaught Exception!";
  }, 1000);
});

promise.catch(function(e) {
  console.log(e);       //This is never called
});

情景5: promise狀態(tài)變?yōu)?b>resove或reject,就凝固了,不會(huì)再改變

console.log(1);
new Promise(function (resolve, reject){
    reject();
    setTimeout(function (){
        resolve();            //not called
    }, 0);
}).then(function(){
    console.log(2);
}, function(){
    console.log(3);
});
console.log(4);

-------output-------
1
4
3
五 結(jié)語(yǔ)

關(guān)于promise就先介紹到這邊了,比較基礎(chǔ),有不足的地方歡迎指出,有更好的也歡迎補(bǔ)充~

參考資料:

https://developer.mozilla.org...

http://liubin.org/promises-book/

http://es6.ruanyifeng.com/#do...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/80545.html

相關(guān)文章

  • 初探 es6 promise

    摘要:是單線程程序,所有代碼都是單線程執(zhí)行。導(dǎo)致的網(wǎng)絡(luò)請(qǐng)求都是異步執(zhí)行,異步執(zhí)行可以通過(guò)回調(diào)函數(shù)實(shí)現(xiàn)秒鐘才能打印,回調(diào)函數(shù)處理異步執(zhí)行的但是推出一種新的方法對(duì)象用于表示一個(gè)異步操作的最終狀態(tài)完成或失敗,以及其返回的值。 javascript是單線程程序,所有代碼都是單線程執(zhí)行。導(dǎo)致javascript的網(wǎng)絡(luò)請(qǐng)求都是異步執(zhí)行,異步執(zhí)行可以通過(guò)回調(diào)函數(shù)實(shí)現(xiàn): setTimeout(callbac...

    paraller 評(píng)論0 收藏0
  • Promise初探

    摘要:可以根據(jù)省份城市和區(qū)對(duì)組件設(shè)置默認(rèn)值。獲取省份獲取城市獲取區(qū)出現(xiàn)層嵌套的回調(diào),這就是傳說(shuō)中的惡魔金字塔。相比回調(diào)嵌套,層次更分明,可讀性強(qiáng)。基本原理學(xué)習(xí)無(wú)論是在異步操作的執(zhí)行之前或執(zhí)行之后,用對(duì)象的方法注冊(cè)回調(diào),回調(diào)都能一致執(zhí)行。 遭遇惡魔金字塔 項(xiàng)目需要,封裝了一個(gè)省市區(qū)的地址選擇器組件。 可以根據(jù)省份id、城市id和區(qū)id對(duì)組件設(shè)置默認(rèn)值。邏輯是這樣的: 獲取省份列表,選中默認(rèn)省...

    mj 評(píng)論0 收藏0
  • Callback到Promise再到Async進(jìn)化初探

    摘要:題外今天嘗試了一下從文件經(jīng)過(guò)再到文件的整個(gè)過(guò)程,這也是這種靜態(tài)博客生成過(guò)程中的一環(huán)。這過(guò)程中,用到的中的系統(tǒng),寫(xiě)的過(guò)程中恰好也經(jīng)歷了從到再到的轉(zhuǎn)變??梢钥吹缴厦娴暮瘮?shù)已經(jīng)非常順序化了,當(dāng)有個(gè)異步函數(shù)回調(diào)時(shí),只需要順序?qū)懢涂梢岳病? 題外:今天嘗試了一下從Markdown文件經(jīng)過(guò)ejs再到html文件的整個(gè)過(guò)程,這也是Hexo這種靜態(tài)博客生成過(guò)程中的一環(huán)。這過(guò)程中,用到的Node中的fs系...

    legendmohe 評(píng)論0 收藏0
  • Mocha測(cè)試初探

    摘要:是測(cè)試用例,表示一個(gè)單獨(dú)的測(cè)試,是測(cè)試的最小單位。第一個(gè)參數(shù)是測(cè)試用例的名稱加應(yīng)該等于,第二個(gè)參數(shù)是一個(gè)實(shí)際執(zhí)行的函數(shù)。這里對(duì)異步代碼進(jìn)行測(cè)試時(shí)需要注意一點(diǎn),默認(rèn)一個(gè)單元測(cè)試最多執(zhí)行超時(shí)會(huì)報(bào)錯(cuò)。 Mocha簡(jiǎn)介: 一個(gè)具有豐富特性的javascript 測(cè)試框架,支持多種斷言庫(kù),異步代碼測(cè)試等,不僅可運(yùn)行在node.js 環(huán)境中還可以運(yùn)行在瀏覽器中。 一、安裝 // 全局安裝 npm ...

    Binguner 評(píng)論0 收藏0
  • Puppeteer初探--爬取并生成《ES6標(biāo)準(zhǔn)入門(mén)》PDF

    摘要:首先介紹是一個(gè)庫(kù),他提供了一組用來(lái)操縱的默認(rèn)也就是無(wú)的,也可以配置為有有點(diǎn)類(lèi)似于,但是官方團(tuán)隊(duì)進(jìn)行維護(hù)的,前景更好。使用,相當(dāng)于同時(shí)具有和的能力,應(yīng)用場(chǎng)景會(huì)非常多。 首先介紹Puppeteer Puppeteer是一個(gè)node庫(kù),他提供了一組用來(lái)操縱Chrome的API(默認(rèn)headless也就是無(wú)UI的chrome,也可以配置為有UI) 有點(diǎn)類(lèi)似于PhantomJS,但Puppet...

    JerryWangSAP 評(píng)論0 收藏0
  • 構(gòu)建 Web 應(yīng)用之 Service Worker 初探

    摘要:誕生之初,是單線程的。當(dāng)接收到服務(wù)端的響應(yīng)之后,便通過(guò)回調(diào)函數(shù)執(zhí)行之后的操作。沖鋒基于事件驅(qū)動(dòng)。擁有攔截請(qǐng)求消息推送靜默更新地理圍欄等服務(wù)??刂茣r(shí)處于兩種狀態(tài)之一終止以節(jié)省內(nèi)存監(jiān)聽(tīng)獲取和消息事件。支持的所有事件五銷(xiāo)毀瀏覽器決定是否銷(xiāo)毀。 這次體驗(yàn)一種新的博客風(fēng)格,我們長(zhǎng)話短說(shuō),針針見(jiàn)血。 showImg(https://segmentfault.com/img/remote/14600...

    voidking 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<