国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

[es6系列]學習Promise

YanceyOfficial / 450人閱讀

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

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

1. 概述

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

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

2. 理解Promise

我們通過一個簡單例子先來感受一下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ù)

我們在通過Promise構(gòu)造函數(shù)實例化一個對象時,會傳遞一個函數(shù)作為參數(shù),那么這個函數(shù)有什么特點?

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

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

console.log("end");

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

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

2.2 resolve/reject

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

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

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

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

resolve就對應著完成態(tài)之后的操作

reject對應著失敗態(tài)之后的操作

2.3 then()

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

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

第一個參數(shù)(函數(shù))對應著完成態(tài)的操作,也就是resolve

第二個參數(shù)(函數(shù))對應著失敗態(tài)的操作,也就是reject

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

2.4 實際案例

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

我們來實現(xiàn)一個異步加載圖片的函數(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");
});

我們在chrome中執(zhí)行,先是傳遞一個有效的url,再傳遞一個無效的url,執(zhí)行的效果為:

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

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

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

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

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

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

舉個例子

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")
});

我們在node環(huán)境下運行以上代碼,執(zhí)行結(jié)果為:

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

3.2 then()

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

那么then()方法的返回值是什么?then方法會返回一個新的Promise實例(注意,不是原來那個Promise,原來那個Promise已經(jīng)承諾過,此時繼續(xù)then就需要新的承諾~~),這樣的設(shè)計的好處就是可以使用鏈式寫法。

還有一個點,就是鏈式中的then方法(第二個開始),它們的resolve中的參數(shù)是什么?答案就是前一個then()中resolve的return語句的返回值。

來一個示例:

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()錯誤處理

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

這個方法其實很簡單,在這里并不想討論它的使用,而是想討論的是Promise中的錯誤的捕抓和處理。

3.3.1 Error對象的傳遞性

Promise對象的Error對象具有冒泡性質(zhì),會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個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)的錯誤。
    console.log(err.toString());
});

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

在第一個then中拋出了一個錯誤,在最后一個Promise對象中可以catch到這個錯誤。

因為有這種方便的錯誤處理機制,所以一般來說不要在then方法里面定義reject狀態(tài)的回調(diào)函數(shù), 而是使用catch方法

3.3.2 vs try/catch

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

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

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

3.3.3 catch()的返回值

沒錯,既然catch()是.then(null, rejection)的別名,那么catch()就會返回一個Promise對象,因此在后面還可以接著調(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);
});

當出錯時,catch會先處理之前的錯誤,然后通過return語句,將值繼續(xù)傳遞給后一個then方法中。

如果沒有報錯,則跳過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對象方法 4.1 Promise.all()

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

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

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

p1, p2, p3的狀態(tài)都為完成態(tài)時,p為完成態(tài)。

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

4.2 Promise.race()

Promise.race方法同樣是將多個Promise實例,包裝成一個新的Promise實例。

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

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

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)有的對象轉(zhuǎn)為Promise對象。

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

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

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

4.3.1 Promise實例

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

4.3.2 thenable對象

參數(shù)是一個thenable對象,也就是說對象是具有then方法的對象,但不是一個Promise實例(就跟類數(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方法會將這個對象轉(zhuǎn)為Promise對象,然后立即執(zhí)行thenable對象中的then方法,因為例子中的thenable對象的then方法中執(zhí)行了resolve,因此會輸出結(jié)果42

4.3.3 其他參數(shù)

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

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

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

需要注意的立即resolve的Promise對象,是在本輪事件循環(huán)的結(jié)束時,而不是下一輪事件循環(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()返回一個新的Promise實例,該實例的狀態(tài)為rejected,對于傳入的參數(shù)的處理跟Promise.resolve類似,就是狀態(tài)都為rejected

5. 兩個實用的方法 5.1 done()

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

這個代碼的實現(xiàn)非常簡單

Promise.prototype.done = function (resolve, reject) {
    this.then(resolve, reject)
        .catch( function (reason) {
            // 拋出一個全局錯誤
            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();

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

5.2 finally()

finally方法用于指定不管Promise對象最后的狀態(tài)如何,都會執(zhí)行的操作,它與done方法最大的區(qū)別就是,它接受一個普通函數(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)劣勢

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

但是,Promise也有一些缺點:

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

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

當處于未完成態(tài)時,無法得知目前進展到哪一個階段。

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

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

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;

        // 默認value是一個Promise,其實這里應該做判斷的
        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之中,有一個異步操作getFoo,它返回的就是一個Promise對象。函數(shù)run用來處理這個Promise對象,并調(diào)用下一個next方法。

7. 來源

個人博客

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

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/91221.html

相關(guān)文章

  • JS筆記

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

    rottengeek 評論0 收藏0
  • ES6-7

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

    mudiyouyou 評論0 收藏0
  • JavaScript 異步

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

    tuniutech 評論0 收藏0
  • ES6系列文章 異步神器async-await

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

    miqt 評論0 收藏0
  • JavasScript重難點知識

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

    forsigner 評論0 收藏0

發(fā)表評論

0條評論

YanceyOfficial

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<