異步的魅力:Promise

當下js最難以處理的的就是異步的任務(wù)會什么時候完成和完成之后的回調(diào)問題虚婿。
而回調(diào)函數(shù)也是最基礎(chǔ)最常用的處理js異步操作的辦法,而無限的回調(diào)會讓代碼的可維護性變得非常差榄棵。難以掌控的觸發(fā)狀態(tài)躲胳,讓你自己寫的代碼當時還可以讀懂,但是過一段時間如果不重新理一下邏輯皮服,估計自己都會忘了。借用一個例子:

  listen( "click", function handler(evt){
    setTimeout( function request(){
        ajax( "http://some.url.1", function response(text){
            if (text == "hello") {
                handler();
            }
            else if (text == "world") {
                request();
            }
        } );
    }, 1000) ;
} );
doSomething();

這種地獄回調(diào)會讓代碼變得多么不可控参咙。

  • 第一步:執(zhí)行l(wèi)istern()
  • 第二步: doSomething()
  • 第三步:1000ms后執(zhí)行ajax()
  • 第四步:ajax完成后龄广,根據(jù)結(jié)果執(zhí)行handler或request
    如果handler或request里面還有這樣的代碼,估計以后維護的同學(xué)來改個功能蕴侧,心里非常的不爽择同。

在你不知道的javascript一書中,對于回調(diào)的信任問題做了闡述 當你使用第三方的庫的方法處理回調(diào)時很有可能遇到以下信任內(nèi)容

image.png

基于這些問題净宵,Promise就橫空出世了敲才。

一、什么是Promise

Promise是異步編程的一種解決方案择葡,它有三種狀態(tài)紧武,分別是pending-進行中、resolved-已完成敏储、rejected-已失敗

當Promise的狀態(tài)又pending轉(zhuǎn)變?yōu)閞esolved或rejected時阻星,會執(zhí)行相應(yīng)的方法,并且狀態(tài)一旦改變已添,就無法再次改變狀態(tài)妥箕,這也是它名字promise-承諾的由來

二、Promise的基本用法

function loadImg(src) {
  let promise = new Promise(function (resolve, reject) {
    // 新建一個img標簽
    let img = document.createElement('img')
    // 圖片加載成功
    img.onload = function () {
      resolve(img)
    }
    // 圖片加載失敗
    img.onerror = function () {
      reject('圖片加載失敗')
     }
    img.src = src
})
return promise
}
let src ='http://img5.imgtn.bdimg.com/it/u=415293130,2419074865&fm=27&gp=0.jpg'
let result = loadImg(src)
result.then((img) => {
  console.log(`加載成功更舞,img寬度=${img.width}`)
}, (text) => {
  console.log(`error:${text}`)
})
  • result把圖片地址傳入loadImg中畦幢,loadImg返回一個Promise對象。
  • 根據(jù)圖片地址加載圖片缆蝉,加載成功就執(zhí)行resolve宇葱,反之失敗就執(zhí)行reject。

then方法接收兩個函數(shù)刊头,一個是成功(狀態(tài)為resolved)的回調(diào)函數(shù)贝搁,一個失敗(狀態(tài)為rejected)的回調(diào)函數(shù)芽偏。

then方法的返回值不是一個promise對象就會被包裝成一個promise對象,所以then方法支持鏈式調(diào)用弦讽。
再來個按順序執(zhí)行的函數(shù):

function taskA() {
  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);

這樣看著是不是非常順眼污尉。

三膀哲、Promise.prototype.then() VS Promise.prototype.catch()

.then()方法使Promise原型鏈上的方法,它包含兩個參數(shù)方法被碗,分別是已成功resolved的回調(diào)和已失敗rejected的回調(diào)

promise.then(
    () => { console.log('this is success callback') },
    () => { console.log('this is fail callback') }
)

.catch()的作用是捕獲Promise的錯誤某宪,與then()的rejected回調(diào)作用幾乎一致。但是由于Promise的拋錯具有冒泡性質(zhì)锐朴,能夠不斷傳遞兴喂,這樣就能夠在下一個catch()中統(tǒng)一處理這些錯誤。同時catch()也能夠捕獲then()中拋出的錯誤焚志,所以建議不要使用then()的rejected回調(diào)衣迷,而是統(tǒng)一使用catch()來處理錯誤

promise.then(
    () => { console.log('this is success callback') }
).catch(
    (err) => { console.log(err) }
)

同樣,catch()中也可以拋出錯誤酱酬,由于拋出的錯誤會在下一個catch中被捕獲處理壶谒,因此可以再添加catch()
使用rejects()方法改變狀態(tài)和拋出錯誤 throw new Error() 的作用是相同的

當狀態(tài)已經(jīng)改變?yōu)閞esolved后,即使拋出錯誤膳沽,也不會觸發(fā)then()的錯誤回調(diào)或者catch()方法

then() 和 catch() 都會返回一個新的Promise對象汗菜,可以鏈式調(diào)用

promise.then(
    () => { console.log('this is success callback') }
).catch(
    (err) => { console.log(err) }
).then(
    ...
).catch(
    ...
)

Promise實例的異步方法和then()中返回promise有什么區(qū)別?

// p1異步方法中返回p2
let p1 = new Promise ( (resolve, reject) => {
    resolve(p2)
} )
let p2 = new Promise ( ... )
// then()中返回promise
let p3 = new Promise ( (resolve, reject) => {
    resolve()
} )
let p4 = new Promise ( ... )
p3.then(
    () => return p4
)

p1異步方法中返回p2

p1的狀態(tài)取決于p2挑社,如果p2為pending陨界,p1將等待p2狀態(tài)的改變,p2的狀態(tài)一旦改變痛阻,p1將會立即執(zhí)行自己對應(yīng)的回調(diào)菌瘪,即then()中的方法針對的依然是p1

then()中返回promise

由于then()本身就會返回一個新的promise,所以后一個then()針對的永遠是一個新的promise录平,但是像上面代碼中我們自己手動返回p4麻车,那么我們就可以在返回的promise中再次通過 resolve() 和 reject() 來改變狀態(tài)

Promise的其他api
Promise.resolve() / Promise.reject()
用來包裝一個現(xiàn)有對象,將其轉(zhuǎn)變?yōu)镻romise對象斗这,但Promise.resolve()會根據(jù)參數(shù)情況返回不同的Promise:

參數(shù)是Promise:原樣返回
參數(shù)帶有then方法:轉(zhuǎn)換為Promise后立即執(zhí)行then方法
參數(shù)不帶then方法动猬、不是對象或沒有參數(shù):返回resolved狀態(tài)的Promise

Promise.reject()會直接返回rejected狀態(tài)的Promise

Promise.all()
參數(shù)為Promise對象數(shù)組,如果有不是Promise的對象表箭,將會先通過上面的Promise.resolve()方法轉(zhuǎn)換

var promise = Promise.all( [p1, p2, p3] )
promise.then(
    ...
).catch(
    ...
)

當p1赁咙、p2、p3的狀態(tài)都變成resolved時免钻,promise才會變成resolved彼水,并調(diào)用then()的已完成回調(diào),但只要有一個變成rejected狀態(tài)极舔,promise就會立刻變成rejected狀態(tài)

Promise.race()
var promise = Promise.race( [p1, p2, p3] )
promise.then(
    ...
).catch(
    ...
)

“競速”方法凤覆,參數(shù)與Promise.all()相同,不同的是拆魏,參數(shù)中的p1盯桦、p2慈俯、p3只要有一個改變狀態(tài),promise就會立刻變成相同的狀態(tài)并執(zhí)行對于的回調(diào)

Promise.done() / Promise. finally()
Promise.done() 的用法類似 .then() ,可以提供resolved和rejected方法,也可以不提供任何參數(shù)拼弃,它的主要作用是在回調(diào)鏈的尾端捕捉前面沒有被 .catch() 捕捉到的錯誤

Promise. finally() 接受一個方法作為參數(shù),這個方法不管promise最終的狀態(tài)是怎樣刑峡,都一定會被執(zhí)行

四、大神實現(xiàn)的Promise源碼

下面是一個網(wǎng)上找的實現(xiàn)Promise的代碼玄柠。

const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
class PromiseDemo {
    constructor(callback) {
        this.status = PENDING;
        this.value = null;
        this.defferd = [];
        setTimeout(
            callback.bind(this, this.resolve.bind(this), this.reject.bind(this)
        ), 10);
    };
    resolve(result) {
        this.status = FULFILLED;
        this.value = result;
        this.done();
    }
    reject(error) {
        this.status = REJECTED;
        this.value = error;
    }
    handle(fn) {
        if (!fn) {
            return;
        }
        var value = this.value;
        var t = this.status;
        var p;
        if (t == PENDING) {
            this.defferd.push(fn);
        } else {
            if (t == FULFILLED && typeof fn.onfulfiled == 'function') {
                p = fn.onfulfiled(value);
            }
            if (t == REJECTED && typeof fn.onrejected == 'function') {
                p = fn.onrejected(value);
            }
            var promise = fn.promise;
            if (promise) {
                if (p && p.constructor == this) {
                    p.defferd = promise.defferd;
                } else {
                    p = this;
                    p.defferd = promise.defferd;
                    this.done();
                }
            }
        }
    }
    done() {
        if (this.status == PENDING) {
            return;
        }
        var defferd = this.defferd;
        for (var i = 0; i < defferd.length; i++) {
            this.handle(defferd[i]);
        }
    }
    then(success, fail) {
        var o = {
            onfulfiled: success,
            onrejected: fail
        };
        var status = this.status;
        o.promise = new this.constructor(function () { });
        if (status == PENDING) {
            this.defferd.push(o);
        } else if (status == FULFILLED || status == REJECTED) {
            this.handle(o);
        }
        return o.promise;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末突梦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子随闪,更是在濱河造成了極大的恐慌阳似,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铐伴,死亡現(xiàn)場離奇詭異撮奏,居然都是意外死亡,警方通過查閱死者的電腦和手機当宴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門畜吊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人户矢,你說我怎么就攤上這事玲献。” “怎么了梯浪?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵捌年,是天一觀的道長。 經(jīng)常有香客問我挂洛,道長礼预,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任虏劲,我火速辦了婚禮托酸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柒巫。我一直安慰自己励堡,他們只是感情好,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布堡掏。 她就那樣靜靜地躺著应结,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泉唁。 梳的紋絲不亂的頭發(fā)上摊趾,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天币狠,我揣著相機與錄音,去河邊找鬼砾层。 笑死,一個胖子當著我的面吹牛贱案,可吹牛的內(nèi)容都是我干的肛炮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼宝踪,長吁一口氣:“原來是場噩夢啊……” “哼侨糟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瘩燥,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤秕重,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后厉膀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溶耘,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年服鹅,在試婚紗的時候發(fā)現(xiàn)自己被綠了凳兵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡企软,死狀恐怖庐扫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仗哨,我是刑警寧澤形庭,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站厌漂,受9級特大地震影響萨醒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜桩卵,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一验靡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雏节,春花似錦胜嗓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至寥粹,卻和暖如春变过,著一層夾襖步出監(jiān)牢的瞬間埃元,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工媚狰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留岛杀,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓崭孤,卻偏偏與公主長得像类嗤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辨宠,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案遗锣,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,708評論 1 56
  • Prepending(進行時),Resolve(成功了)嗤形,Reject(失敗了)精偿,then......等 1.Pr...
    _菩提本無樹_閱讀 49,078評論 0 21
  • Promise對象是一種解決異步問題的方法,還有的解決方案是asyns 和 await (es7) 這么是目前的終...
    站在大神的肩膀上看世界閱讀 1,266評論 0 6
  • 目錄:Promise 的含義基本用法Promise.prototype.then()Promise.prototy...
    BluesCurry閱讀 1,494評論 0 8
  • 小曼: 老師好赋兵,我男朋友有個女閨蜜笔咽,沒認識我之前那女的就愛摟他,后來認識我確定男女關(guān)系后毡惜,他女閨蜜還摟他拓轻,我就跟男...
    冷月公子閱讀 1,105評論 0 1