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

資訊專欄INFORMATION COLUMN

[es6系列]學(xué)習(xí)Promise

YanceyOfficial / 453人閱讀

摘要:概述在之前,在中的異步編程都是采用回調(diào)函數(shù)和事件的方式,但是這種編程方式在處理復(fù)雜業(yè)務(wù)的情況下,很容易出現(xiàn)回調(diào)地獄,使得代碼很難被理解和維護(hù)。如果不設(shè)置回調(diào)函數(shù),內(nèi)部的錯(cuò)誤不會(huì)反應(yīng)到外部。

本文是基于對(duì)阮一峰的Promise文章的學(xué)習(xí)整理筆記,整理了文章的順序、增加了更多的例子,使其更好理解。

1. 概述

在Promise之前,在js中的異步編程都是采用回調(diào)函數(shù)和事件的方式,但是這種編程方式在處理復(fù)雜業(yè)務(wù)的情況下,很容易出現(xiàn)callback hell(回調(diào)地獄),使得代碼很難被理解和維護(hù)。

Promise就是改善這種情形的異步編程的解決方案,它由社區(qū)最早提出和實(shí)現(xiàn),es6將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法,并且提供了一個(gè)原生的對(duì)象Promise。

2. 理解Promise

我們通過一個(gè)簡(jiǎn)單例子先來感受一下Promise。

var p = new Promise(function (resolve, reject) {
    // ...
    if(/* 異步操作成功 */){
        resolve(ret);
    } else {
        reject(error);
    }
});

p.then(function (value) {
    // 完成態(tài)
}, function (error) {
    // 失敗態(tài)
});

我們需要關(guān)注的是

Promise的構(gòu)造函數(shù)

resolve() , reject()

then()

2.1 Promise構(gòu)造函數(shù)

我們?cè)谕ㄟ^Promise構(gòu)造函數(shù)實(shí)例化一個(gè)對(duì)象時(shí),會(huì)傳遞一個(gè)函數(shù)作為參數(shù),那么這個(gè)函數(shù)有什么特點(diǎn)?

答案就是在新建一個(gè)Promise后,這個(gè)函數(shù)會(huì)立即執(zhí)行。

let promise = new Promise(function (reslove, reject) {
    console.log("Promise");
});

console.log("end");

執(zhí)行結(jié)果如下:

可以看到是先輸出了Promise,再輸出了end。

2.2 resolve/reject

在Promise中,對(duì)一個(gè)異步操作做出了抽象的定義,Promise操作只會(huì)處在3種狀態(tài)的一種,他們之間的轉(zhuǎn)化如圖所示

注意,這種狀態(tài)的改變只會(huì)出現(xiàn)從未完成態(tài)向完成態(tài)或失敗態(tài)轉(zhuǎn)化,不能逆反。完成態(tài)和失敗態(tài)不能互相轉(zhuǎn)化,而且,狀態(tài)一旦轉(zhuǎn)化,將不能更改。

只有異步操作的結(jié)果可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個(gè)狀態(tài)。這也是Promise這個(gè)名字的由來,它的英語意思是承諾,表示其他手段無法改變。

在聲明一個(gè)Promise對(duì)象實(shí)例時(shí),我們傳入的匿名函數(shù)參數(shù)中:

resolve就對(duì)應(yīng)著完成態(tài)之后的操作

reject對(duì)應(yīng)著失敗態(tài)之后的操作

2.3 then()

那么問題來了,then()方法有什么作用?resolve和reject又是從哪里傳遞過來的?

其實(shí)這兩個(gè)問題是一個(gè)問題,在實(shí)例化一個(gè)Promise對(duì)象之后,我們調(diào)用該對(duì)象實(shí)例的then()方法傳遞的兩個(gè)參數(shù)中:

第一個(gè)參數(shù)(函數(shù))對(duì)應(yīng)著完成態(tài)的操作,也就是resolve

第二個(gè)參數(shù)(函數(shù))對(duì)應(yīng)著失敗態(tài)的操作,也就是reject

那就是說,在Promise中是通過then()方法來指定處理異步操作結(jié)果的方法。

2.4 實(shí)際案例

到這里我們明白了Promise的語法,也了解了Promise中函數(shù)是如何執(zhí)行的,結(jié)合一個(gè)實(shí)際的案例,來加深對(duì)Promise的理解。

我們來實(shí)現(xiàn)一個(gè)異步加載圖片的函數(shù)

function loadImageAsync(url) {
    return new Promise(function (reslove, reject) {
        var img = new Image();
        img.onload = function () {
            reslove();
        }
        img.onerror = function () {
            reject();
        }
        console.log("loading image");
        img.src = url;
    });
}
var loadImage1 = loadImageAsync("https://www.google.co.jp/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png");
loadImage1.then(function success() {
    console.log("success");
}, function fail() {
    console.log("fail");
});

var loadImage2 = loadImageAsync("1.png");
loadImage2.then(function success() {
    console.log("success");
}, function fail() {
    console.log("fail");
});

我們?cè)赾hrome中執(zhí)行,先是傳遞一個(gè)有效的url,再傳遞一個(gè)無效的url,執(zhí)行的效果為:

3. Promise進(jìn)階 3.1 resolve/reject的參數(shù)

reject函數(shù)的參數(shù)一般來說是Error對(duì)象的實(shí)例,而resolve函數(shù)的參數(shù)除了正常的值外,還可能是另一個(gè)Promise實(shí)例,表示異步操作的結(jié)果有可能是一個(gè)值,也有可能是另一個(gè)異步操作。

var p1 = new Promise( function(resolve, reject) {
    // ...
});

var p2 = new Promise( function(resolve, reject) {
    // ...
    resolve(p1);
});

代碼分析:p1和p2都是Promise的實(shí)例,p2中的resolve方法將p1作為參數(shù),即一個(gè)異步操作的結(jié)果是返回另一個(gè)異步操作。

注意,這時(shí)p1的狀態(tài)就會(huì)傳遞給p2,也就是說,p1的狀態(tài)決定了p2的狀態(tài),他們之間的關(guān)系是

舉個(gè)例子

console.time("Promise example start")
var p1 = new Promise( (resolve, reject) => {
    setTimeout(() => resolve("hi"), 3000);
});

var p2 = new Promise( (resolve, reject) => {
    setTimeout(() => resolve(p1), 10);
});

p2.then( ret => {
    console.log(ret);
    console.timeEnd("Promise example end")
});

我們?cè)趎ode環(huán)境下運(yùn)行以上代碼,執(zhí)行結(jié)果為:

從執(zhí)行時(shí)間可以看到,p2會(huì)等待p1的執(zhí)行結(jié)果,然后再執(zhí)行,從輸出hi可以看到p1完成狀態(tài)轉(zhuǎn)變之后,傳遞給resolve(或者reject)的結(jié)果會(huì)傳遞給p2中的resolve。

3.2 then()

從上面的例子,我們可以了解到then()方法是Promise實(shí)例的方法,即Promise.prototype上的,它的作用是為Promise實(shí)例添加狀態(tài)改變時(shí)的回調(diào)函數(shù),這個(gè)方法的第一個(gè)參數(shù)是resolved狀態(tài)的回調(diào)函數(shù),第二個(gè)參數(shù)(可選)是rejected狀態(tài)的回調(diào)函數(shù)。

那么then()方法的返回值是什么?then方法會(huì)返回一個(gè)新的Promise實(shí)例(注意,不是原來那個(gè)Promise,原來那個(gè)Promise已經(jīng)承諾過,此時(shí)繼續(xù)then就需要新的承諾~~),這樣的設(shè)計(jì)的好處就是可以使用鏈?zhǔn)綄懛ā?/p>

還有一個(gè)點(diǎn),就是鏈?zhǔn)街械?b>then方法(第二個(gè)開始),它們的resolve中的參數(shù)是什么?答案就是前一個(gè)then()中resolve的return語句的返回值。

來一個(gè)示例:

var p1 = new Promise( (resolve, reject) => {
    setTimeout(() => resolve("p1"), 10);
});

p1.then( ret => {
    console.log(ret);
    return "then1";
}).then( ret => {
    console.log(ret);
    return "then2";
}).then( ret => {
    console.log(ret);
});

在node環(huán)境下執(zhí)行,執(zhí)行結(jié)果為:

3.3 catch()錯(cuò)誤處理

catch()方法是Promise實(shí)例的方法,即Promise.prototype上的屬性,它其實(shí)是.then(null, rejection)的簡(jiǎn)寫,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)。

這個(gè)方法其實(shí)很簡(jiǎn)單,在這里并不想討論它的使用,而是想討論的是Promise中的錯(cuò)誤的捕抓和處理。

3.3.1 Error對(duì)象的傳遞性

Promise對(duì)象的Error對(duì)象具有冒泡性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。也就是說,錯(cuò)誤總是會(huì)被下一個(gè)catch語句捕獲,示例代碼如下:

var p = new Promise( (resolve, reject) => {
    setTimeout(() => resolve("p1"), 10);
});

p.then( ret => {
    console.log(ret);
    throw new Error("then1");
    return "then1";
}).then( ret => {
    console.log(ret);
    throw new Error("then2");
    return "then2";
}).catch( err => {
    // 可以捕抓到前面的出現(xiàn)的錯(cuò)誤。
    console.log(err.toString());
});

執(zhí)行結(jié)果如下

在第一個(gè)then中拋出了一個(gè)錯(cuò)誤,在最后一個(gè)Promise對(duì)象中可以catch到這個(gè)錯(cuò)誤。

因?yàn)橛羞@種方便的錯(cuò)誤處理機(jī)制,所以一般來說不要在then方法里面定義reject狀態(tài)的回調(diào)函數(shù), 而是使用catch方法

3.3.2 vs try/catch

跟傳統(tǒng)的try/catch不同的是,如果沒有使用catch方法指定錯(cuò)誤處理回調(diào)函數(shù),則Promise對(duì)象拋出的錯(cuò)誤不會(huì)傳遞到外層代碼(在chrome會(huì)報(bào)錯(cuò))

Node.js有一個(gè)unhandledRejection事件,專門監(jiān)聽未捕獲的reject錯(cuò)誤。以下代碼就是在node環(huán)境下運(yùn)行。

var p = new Promise((resolve, reject) => {
    resolve(x + 2);
});
p.then( () => {
    console.log("nothing");
});

3.3.3 catch()的返回值

沒錯(cuò),既然catch()是.then(null, rejection)的別名,那么catch()就會(huì)返回一個(gè)Promise對(duì)象,因此在后面還可以接著調(diào)用then方法,示例代碼如下:

var p = new Promise((resolve, reject) => {
    resolve(x + 2);
});
p.then( () => {
    console.log("nothing");
}).catch( err => {
    console.log(err.toString());
    return "catch";
}).then( ret => {
    console.log(ret);
});

當(dāng)出錯(cuò)時(shí),catch會(huì)先處理之前的錯(cuò)誤,然后通過return語句,將值繼續(xù)傳遞給后一個(gè)then方法中。

如果沒有報(bào)錯(cuò),則跳過catch,示例如下:

var p = new Promise((resolve, reject) => {
    resolve("p");
});
p.then( ret => {
    console.log(ret);
    return "then1";
}).catch( err => {
    console.log(err.toString());
    return "catch";
}).then( ret => {
    console.log(ret);
});

4. Promise對(duì)象方法 4.1 Promise.all()

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

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

新的Promise實(shí)例p的狀態(tài)由p1, p2, p3決定:

當(dāng)p1, p2, p3的狀態(tài)都為完成態(tài)時(shí),p為完成態(tài)。

p1, p2, p3中任一一個(gè)狀態(tài)為失敗態(tài),則p為失敗態(tài)。

4.2 Promise.race()

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

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

不同的是,只要p1, p2, p3中任意一個(gè)實(shí)例率先改變狀態(tài),則p的狀態(tài)就跟著改變,而且狀態(tài)由率先改變的實(shí)例決定。

var p = Promise.race([
    new Promise(resolve => {
        setTimeout(() => resolve("p1"), 10000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => reject(new Error("time out")), 10);
    })
]);
p.then( ret => console.log(ret))
    .catch( err => console.log(err.toString()));

4.3 Promise.resolve()

Promise.resolve()可以將現(xiàn)有的對(duì)象轉(zhuǎn)為Promise對(duì)象。

var p = Promise.resolve("p");

// 相當(dāng)于
var p = new Promise(resolve => resolve("p"));

比較有意思的是Promise.resolve()會(huì)根據(jù)參數(shù)類型進(jìn)行相應(yīng)的處理,分幾種情況討論。

4.3.1 Promise實(shí)例

參數(shù)是一個(gè)Promise實(shí)例,那么Promise.resolve將不做任何處理,直接返回這個(gè)實(shí)例。

4.3.2 thenable對(duì)象

參數(shù)是一個(gè)thenable對(duì)象,也就是說對(duì)象是具有then方法的對(duì)象,但不是一個(gè)Promise實(shí)例(就跟類數(shù)組和數(shù)組的關(guān)系一樣),例如

let thenable = {
    then : function (resolve, reject) {
        resolve(42);
    }
};

let p = Promise.resolve(thenable);
p.then( ret => console.log(ret)); // 42

Promise.resolve方法會(huì)將這個(gè)對(duì)象轉(zhuǎn)為Promise對(duì)象,然后立即執(zhí)行thenable對(duì)象中的then方法,因?yàn)槔又械膖henable對(duì)象的then方法中執(zhí)行了resolve,因此會(huì)輸出結(jié)果42。

4.3.3 其他參數(shù)

如果參數(shù)是一個(gè)原始值,或者不具有then方法的對(duì)象,則Promise.resolve方法返回一個(gè)新的Promise對(duì)象,狀態(tài)為resolve,然后直接將該參數(shù)傳遞給resolve方法。

var p = Promise.resolve("p");
p.then( ret => console.log(ret)); // p
4.3.4 不帶任何參數(shù)

Promise.resolve方法不帶參數(shù)時(shí),會(huì)直接返回一個(gè)resolve狀態(tài)的Promise對(duì)象。

需要注意的立即resolve的Promise對(duì)象,是在本輪事件循環(huán)的結(jié)束時(shí),而不是下一輪事件循環(huán)的開始執(zhí)行。示例代碼:

setTimeout(() => console.log("3"), 0);
var p = Promise.resolve();
p.then(() => console.log("2"));
console.log("1");

輸出結(jié)果為:

4.4 Promise.reject()

Promise.reject()返回一個(gè)新的Promise實(shí)例,該實(shí)例的狀態(tài)為rejected,對(duì)于傳入的參數(shù)的處理跟Promise.resolve類似,就是狀態(tài)都為rejected。

5. 兩個(gè)實(shí)用的方法 5.1 done()

Promise對(duì)象的回調(diào)鏈,不管以then方法或者catch方法結(jié)尾,要是最后一個(gè)方法拋出錯(cuò)誤,都有可能無法捕捉到,因?yàn)镻romise內(nèi)部的錯(cuò)誤不會(huì)冒泡到全局,因此,我們可以提供一個(gè)done方法,總是處理回調(diào)鏈的尾端,保證拋出任何可能出現(xiàn)的錯(cuò)誤。

這個(gè)代碼的實(shí)現(xiàn)非常簡(jiǎn)單

Promise.prototype.done = function (resolve, reject) {
    this.then(resolve, reject)
        .catch( function (reason) {
            // 拋出一個(gè)全局錯(cuò)誤
            setTimeout( () => { throw reason }, 0);
        });
}

// 使用示例
var p = new Promise( (resolve, reject) => {
    resolve("p");
});
p.then(ret => {
    console.log(ret);
    return "then1";
}).catch( err => {
    console.log(err.toString());
}).then( ret => {
    console.log(ret);
    return "then2";
}).then( ret => {
    console.log(ret);
    x + 2;
}).done();

這里為什么可以在全局拋出一個(gè)錯(cuò)誤?原因就是setTimeout中的回調(diào)函數(shù)是在全局作用域中執(zhí)行的,因此拋出的錯(cuò)誤就是在全局作用域上。

5.2 finally()

finally方法用于指定不管Promise對(duì)象最后的狀態(tài)如何,都會(huì)執(zhí)行的操作,它與done方法最大的區(qū)別就是,它接受一個(gè)普通函數(shù)作為參數(shù),該函數(shù)不管怎么樣都必須執(zhí)行。

Promise.prototype.finally = function (callback) {
    let P = this.constructor;
    return this.then(
        ret => P.resolve(callback()).then( () => ret),
        err => P.resolve(callback()).then( () => {throw reason })
    );
};
5. Promise的優(yōu)劣勢(shì)

從上面幾個(gè)小節(jié)綜合來看,可以看到Promise其實(shí)就是做了一件事情,那就是對(duì)異步操作進(jìn)行了封裝,然后可以將異步操作以同步的流程表達(dá)出來,避免了層層嵌套的回調(diào)函數(shù),同時(shí)提供統(tǒng)一的接口,使得控制異步操作更加容易。

但是,Promise也有一些缺點(diǎn):

無法取消Promise,一旦新建它就會(huì)立即執(zhí)行,無法中途取消。

如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部的錯(cuò)誤不會(huì)反應(yīng)到外部。

當(dāng)處于未完成態(tài)時(shí),無法得知目前進(jìn)展到哪一個(gè)階段。

6. Promise與generator的結(jié)合

使用Generator函數(shù)來管理流程,遇到異步操作的時(shí)候,通常返回一個(gè)Promise對(duì)象。

function getFoo() {
    return new Promise( resolve => resolve("foo"));
}

var g = function * () {
    try {
        var foo = yield getFoo();
        console.log(foo);
    } catch(e){}
}

function run(generator) {
    var it = generator();

    function go(result) {
        if(result.done) return result.value;

        // 默認(rèn)value是一個(gè)Promise,其實(shí)這里應(yīng)該做判斷的
        if(!(result.value instanceof Promise)){
            throw Error("yield must follow an instanceof Promise");
        }
        return result.value.then(
            ret => go(it.next(ret))
        ).catch(err => go(it.throw(err)));
    }

    go(it.next());
}

run(g);

上面代碼的Generator函數(shù)g之中,有一個(gè)異步操作getFoo,它返回的就是一個(gè)Promise對(duì)象。函數(shù)run用來處理這個(gè)Promise對(duì)象,并調(diào)用下一個(gè)next方法。

7. 來源

個(gè)人博客

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

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

相關(guān)文章

  • JS筆記

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個(gè)方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識(shí)之 HTTP 協(xié)議 詳細(xì)介紹 HTT...

    rottengeek 評(píng)論0 收藏0
  • ES6-7

    摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評(píng)論0 收藏0
  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過...

    tuniutech 評(píng)論0 收藏0
  • ES6系列文章 異步神器async-await

    摘要:有兩個(gè)陌生的關(guān)鍵字,同時(shí)函數(shù)執(zhí)行結(jié)果似乎返回了一個(gè)對(duì)象。用來表示函數(shù)是異步的,定義的函數(shù)會(huì)返回一個(gè)對(duì)象,可以使用方法添加回調(diào)函數(shù)。如果的是對(duì)象會(huì)造成異步函數(shù)停止執(zhí)行并且等待的解決如果等的是正常的表達(dá)式則立即執(zhí)行。 視頻講解 關(guān)于異步處理,ES5的回調(diào)使我們陷入地獄,ES6的Promise使我們脫離魔障,終于、ES7的async-await帶我們走向光明。今天就來學(xué)習(xí)一下 async-a...

    miqt 評(píng)論0 收藏0
  • JavasScript重難點(diǎn)知識(shí)

    摘要:忍者級(jí)別的函數(shù)操作對(duì)于什么是匿名函數(shù),這里就不做過多介紹了。我們需要知道的是,對(duì)于而言,匿名函數(shù)是一個(gè)很重要且具有邏輯性的特性。通常,匿名函數(shù)的使用情況是創(chuàng)建一個(gè)供以后使用的函數(shù)。 JS 中的遞歸 遞歸, 遞歸基礎(chǔ), 斐波那契數(shù)列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果...

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

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

0條評(píng)論

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