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

資訊專欄INFORMATION COLUMN

promise介紹--基礎篇

tanglijun / 1198人閱讀

摘要:請求的傳統(tǒng)寫法改為后的寫法很顯然,我們把異步中使用回調函數(shù)的場景改為了等函數(shù)鏈式調用的方式。數(shù)組中第一個元素是異步的,第二個是非異步,會立即改變狀態(tài),所以新對象會立即改變狀態(tài)并把傳遞給成功時的回調函數(shù)。

前言

Promise,相信每一個前端工程師都或多或少地在項目中都是用過,畢竟它早已不是一個新名詞。ES6中已經(jīng)原生對它加以支持,在caniuse中搜索一下Promise,發(fā)現(xiàn)新版的chrome和firefox也已經(jīng)支持。但是低版本的瀏覽器我們可以使用es6-promise這個polyfill庫來加以兼容。

暫且不談awaitasync,在Google或百度或360搜索等搜索引擎、或者在segmentfault等社區(qū)中,我們可以搜到一大把介紹promise的文章,畢竟它已經(jīng)出現(xiàn)了很長時間,早已有很多大神分析講解過。

我也看了一些文章,但是感覺都沒有達到想要的效果。所以決定自己開一個小系列文章學習講解一下promise的原理,以及實現(xiàn),最后再談一談與之聯(lián)系密切的Deferred對象。

本文是該系列的第一篇文章,主要先讓大家對Promise有一個基本的認識。

promise簡介

Promise的出現(xiàn),原本是為了解決回調地獄的問題。所有人在講解Promise時,都會以一個ajax請求為例,此處我們也用一個簡單的ajax的例子來帶大家看一下Promise是如何使用的。

ajax請求的傳統(tǒng)寫法:

getData(method, url, successFun, failFun){
  var xmlHttp = new XMLHttpRequest();
  xmlHttp.open(method, url);
  xmlHttp.send();
  xmlHttp.onload = function () {
    if (this.status == 200 ) {
      successFun(this.response);
    } else {
      failFun(this.statusText);
    }
  };
  xmlHttp.onerror = function () {
    failFun(this.statusText);
  };
}

改為promise后的寫法:

getData(method, url){
  var promise = new Promise(function(resolve, reject){
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open(method, url);
    xmlHttp.send();
    xmlHttp.onload = function () {
      if (this.status == 200 ) {
        resolve(this.response);
      } else {
        reject(this.statusText);
      }
    };
    xmlHttp.onerror = function () {
      reject(this.statusText);
    };
  })
  return promise;
}

getData("get","www.xxx.com").then(successFun, failFun)

很顯然,我們把異步中使用回調函數(shù)的場景改為了.then().catch()等函數(shù)鏈式調用的方式?;?b>promise我們可以把復雜的異步回調處理方式進行模塊化。

下面,我們就來介紹一下Promise到底是個什么東西?它是如何做到的?

Promise的原理分析

其實promise原理說起來并不難,它內部有三個狀態(tài),分別是pending,fulfilledrejected

pending是對象創(chuàng)建后的初始狀態(tài),當對象fulfill(成功)時變?yōu)?b>fulfilled,當對象reject(失?。r變?yōu)?b>rejected。且只能從pengding變?yōu)?b>fulfilled或rejected ,而不能逆向或從fulfilled變?yōu)?b>rejected 、從rejected變?yōu)?b>fulfilled。如圖所示:

Promise實例方法介紹

Promise對象擁有兩個實例方法then()catch()。

從前面的例子中可以看到,成功和失敗的回調函數(shù)我們是通過then()添加,在promise狀態(tài)改變時分別調用。promise構造函數(shù)中通常都是異步的,所以then方法往往都先于resolvereject方法執(zhí)行。所以promise內部需要有一個存儲fulfill時調用函數(shù)的數(shù)組和一個存儲reject時調用函數(shù)的數(shù)組。

從上面的例子中我們還可以看到then方法可以接收兩個參數(shù),且通常都是函數(shù)(非函數(shù)時如何處理下一篇文章中會詳細介紹)。第一個參數(shù)會添加到fulfill時調用的數(shù)組中,第二個參數(shù)添加到reject時調用的數(shù)組中。當promise狀態(tài)fulfill時,會把resolve(value)中的value值傳給調用的函數(shù)中,同理,當promise狀態(tài)reject時,會把reject(reason)中的reason值傳給調用的函數(shù)。例:

var p = new Promise(function(resolve, reject){
    resolve(5)
}).then(function(value){
    console.log(value) //5
})

var p1 = new Promise(function(resolve, reject){
    reject(new Error("錯誤"))
}).then(function(value){
    console.log(value)
}, function(reason){
    console.log(reason) //Error: 錯誤(…)
})

then方法會返回一個新的promise,下面的例子中p == p1將返回false,說明p1是一個全新的對象。

var p = new Promise(function(resolve, reject){
    resolve(5)
})
var p1 = p.then(function(value){
    console.log(value)
})
p == p1 // false

這也是為什么then是可以鏈式調用的,它是在新的對象上添加成功或失敗的回調,這與jQuery中的鏈式調用不同。

那么新對象的狀態(tài)是基于什么改變的呢?是不是說如果p的狀態(tài)fulfill,后面的then創(chuàng)建的新對象都會成功;或者說如果p的狀態(tài)reject,后面的then創(chuàng)建的新對象都會失敗?

var p = new Promise(function(resolve, reject){
    resolve(5)
})
var p1 = p.then(function(value){
    console.log(value)   // 5
}).then(function(value){
    console.log("fulfill " + value)   // fulfill undefined
}, function(reason){
    console.log("reject " + reason)   
})

上面的例子會打印出5和"fulfill undefined"說明它的狀態(tài)變?yōu)槌晒?。那如果我們?b>p1的then方法中拋出異常呢?

var p = new Promise(function(resolve, reject){
    resolve(5)
})
var p1 = p.then(function(value){
    console.log(value)   // 5
    throw new Error("test")
}).then(function(value){
    console.log("fulfill " + value)
}, function(reason){
    console.log("reject " + reason)   // reject Error: test
})

理所當然,新對象肯定會失敗。

反過來如果p失敗了,會是什么樣的呢?

var p = new Promise(function(resolve, reject){
    reject(5)
})
var p1 = p.then(undefined, function(value){
    console.log(value)   // 5
}).then(function(value){
    console.log("fulfill " + value)   // fulfill undefined
}, function(reason){
    console.log("reject " + reason)
})

說明新對象狀態(tài)不會受到前一個對象狀態(tài)的影響。

再來看如下代碼:

var p = new Promise(function(resolve, reject){
    reject(5)
})
var p1 = p.then(function(value){
    console.log(value) 
})
var p2 = p1.then(function(value){
    console.log("fulfill " + value)
}, function(reason){
    console.log("reject " + reason)   // reject 5
})

我們發(fā)現(xiàn)p1的狀態(tài)變?yōu)?b>rejected,從而觸發(fā)了then方法第二個參數(shù)的函數(shù)。這似乎與我們之前提到的有差異啊,p1的狀態(tài)受到了p的狀態(tài)的影響。

再來看一個例子:

var p = new Promise(function(resolve, reject){
    resolve(5)
})
var p1 = p.then(undefined, function(value){
    console.log(value) 
})
var p2 = p1.then(function(value){
    console.log("fulfill " + value)   // fulfill 5
}, function(reason){
    console.log("reject " + reason)   
})

細心的人可能會發(fā)現(xiàn),該例子中then第一個參數(shù)是undefined,且value值5被傳到了p1成功時的回調函數(shù)中。上面那個例子中then的第二個參數(shù)是undefined,同樣reason值也傳到了p1失敗時的回調函數(shù)中。這是因當對應的參數(shù)不為函數(shù)時,會將前一promise的狀態(tài)和值傳遞下去。

promise含有一個實例方法catch,從名字上我們就看得出來,它和異常有千絲萬縷的關系。其實catch(onReject)方法等價于then(undefined, onReject),也就是說如下兩種情況是等效的。

new Promise(function(resolve, reject){
    reject(new Error("error"))
}).then(undefined, function(reason){
    console.log(reason) // Error: error(…)
})

new Promise(function(resolve, reject){
    reject(new Error("error"))
}).catch(function(reason){
    console.log(reason) // Error: error(…)
})

我們提到參數(shù)不為函數(shù)時會把值和狀態(tài)傳遞下去。所以我們可以在多個then之后添加一個catch方法,這樣前面只要reject或拋出異常,都會被最后的catch方法處理。

new Promise(function(resolve, reject){
    resolve(5)
}).then(function(value){
    taskA()
}).then(function(value){
    taskB()
}).then(function(value){
    taskC()
}).catch(function(reason){
    console.log(reason)
})
Promise的靜態(tài)方法

Promise還有四個靜態(tài)方法,分別是resolve、reject、allrace,下面我們一一介紹。

除了通過new Promise()的方式,我們還有兩種創(chuàng)建Promise對象的方法:

Promise.resolve() 它相當于創(chuàng)建了一個立即resolve的對象。如下兩段代碼作用相同:

Promise.resolve(5)

new Promise(function(resolve){
    resolve(5)
})

它使得promise對象直接resolve,并把5傳到后面then添加的成功函數(shù)中。

Promise.resolve(5).then(function(value){
    console.log(value) // 5
})

Promise.reject() 很明顯它相當于創(chuàng)建了一個立即reject的對象。如下兩段代碼作用相同:

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

new Promise(function(resolve, reject){
    reject(new Error("error"))
})

它使得promise對象直接reject,并把error傳到后面catch添加的函數(shù)中。

Promise.reject(new Error("error")).catch(function(reason){
    console.log(reason) // Error: error(…)
})

Promise.all() 它接收一個promise對象組成的數(shù)組作為參數(shù),并返回一個新的promise對象。

當數(shù)組中所有的對象都resolve時,新對象狀態(tài)變?yōu)?b>fulfilled,所有對象的resolvevalue依次添加組成一個新的數(shù)組,并以新的數(shù)組作為新對象resolvevalue,例:

Promise.all([Promise.resolve(5), 
  Promise.resolve(6), 
  Promise.resolve(7)]).then(function(value){
    console.log("fulfill", value)  // fulfill [5, 6, 7]
}, function(reason){
    console.log("reject",reason)
})

當數(shù)組中有一個對象reject時,新對象狀態(tài)變?yōu)?b>rejected,并以當前對象rejectreason作為新對象rejectreason。

Promise.all([Promise.resolve(5), 
  Promise.reject(new Error("error")), 
  Promise.resolve(7),
  Promise.reject(new Error("other error"))
  ]).then(function(value){
    console.log("fulfill", value)
}, function(reason){
    console.log("reject", reason)  // reject Error: error(…)
})

那當數(shù)組中,傳入了非promise對象會如何呢?

Promise.all([Promise.resolve(5), 
  6,
  true,
  "test",
  undefined,
  null,
  {a:1},
  function(){},
  Promise.resolve(7)
  ]).then(function(value){
    console.log("fulfill", value)  // fulfill [5, 6, true, "test", undefined, null, Object, function, 7]
}, function(reason){
    console.log("reject", reason)
})

我們發(fā)現(xiàn),當傳入的值為數(shù)字、boolean、字符串、undefined、null、{a:1}、function(){}等非promise對象時,會依次把它們添加到新對象resolve時傳遞的數(shù)組中。

那數(shù)組中的多個對象是同時調用,還是一個接一個的依次調用呢?我們再看個例子

function timeout(time) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(time);
        }, time);
    });
}
console.time("promise")
Promise.all([
    timeout(10),
    timeout(60),
    timeout(100)
]).then(function (values) {
    console.log(values); [10, 60, 100]
    console.timeEnd("promise");   // 107ms 
});

由此我們可以看出,傳入的多個對象幾乎是同時執(zhí)行的,因為總的時間略大于用時最長的一個對象resolve的時間。

Promise.race() 它同樣接收一個promise對象組成的數(shù)組作為參數(shù),并返回一個新的promise對象。

Promise.all()不同,它是在數(shù)組中有一個對象(最早改變狀態(tài))resolvereject時,就改變自身的狀態(tài),并執(zhí)行響應的回調。

Promise.race([Promise.resolve(5), 
  Promise.reject(new Error("error")), 
  Promise.resolve(7)]).then(function(value){
    console.log("fulfill", value)  // fulfill 5
}, function(reason){
    console.log("reject",reason)
})

Promise.race([Promise.reject(new Error("error")), 
  Promise.resolve(7)]).then(function(value){
    console.log("fulfill", value) 
}, function(reason){
    console.log("reject",reason) //reject Error: error(…)
})

且當數(shù)組中有非異步Promise對象或有數(shù)字、boolean、字符串、undefined、null、{a:1}、function(){}等非Promise對象時,都會直接以該值resolve。

Promise.race([new Promise((resolve)=>{
    setTimeout(()=>{
        resolve(1)
    },100)}),
  Promise.resolve(5), 
  "test",
  Promise.reject(new Error("error")), 
  Promise.resolve(7)]).then(function(value){
    console.log("fulfill", value)  // fulfill 5
}, function(reason){
    console.log("reject",reason)
})
// fulfill 5

數(shù)組中第一個元素是異步的Promise,第二個是非異步Promise,會立即改變狀態(tài),所以新對象會立即改變狀態(tài)并把5傳遞給成功時的回調函數(shù)。

那么問題又來了,既然數(shù)組中第一個元素成功或失敗就會改變新對象的狀態(tài),那數(shù)組中后面的對象是否會執(zhí)行呢?

function timeout(time) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log(time)
            resolve(time);
        }, time);
    });
}
console.time("promise")
Promise.race([
    timeout(10),
    timeout(60),
    timeout(100)
]).then(function (values) {
    console.log(values); [10, 60, 100]
    console.timeEnd("promise");   // 107ms
});

// 結果依次為
// 10
// 10
// promise: 11.1ms
// 60
// 100

說明即使新對象的狀態(tài)改變,數(shù)組中后面的promise對象還會執(zhí)行完畢,其實Promise.all()中即使前面reject了,所有的對象也都會執(zhí)行完畢。規(guī)范中,promise對象執(zhí)行是不可以中斷的。

補充

promise對象即使立馬改變狀態(tài),它也是異步執(zhí)行的。如下所示:

Promise.resolve(5).then(function(value){
  console.log("后打出來", value)
});
console.log("先打出來")

// 結果依次為
// 先打出來
// 后打出來 5

但還有一個有意思的例子,如下:

setTimeout(function(){console.log(4)},0);
new Promise(function(resolve){
    console.log(1)
    for( var i=0 ; i<10000 ; i++ ){
        i==9999 && resolve()
    }
    console.log(2)
}).then(function(){
    console.log(5)
});
console.log(3);

結果是 1 2 3 5 4,命名4是先添加到異步隊列中的,為什么結果不是1 2 3 4 5呢?這個涉及到Event loop,后面我會多帶帶講一下。

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

轉載請注明本文地址:http://systransis.cn/yun/91230.html

相關文章

  • Promise介紹--Deferred及jQuery

    摘要:我們稱為回調對象,它內部會維護一個數(shù)組,我們可以向其中添加若干個回調函數(shù),然后在某一條件下觸發(fā)執(zhí)行。第一次之后,再次新的回調函數(shù)時,自動執(zhí)行回調。當前面的回調函數(shù)返回時,終止后面的回調繼續(xù)執(zhí)行。 最近懶癌發(fā)作,說好的系列文章,寫了一半,一直懶得寫,今天補上一篇。 Deferred 我們在使用promise對象時,總會提到一個與它關系密切的對象——Deferred。其實Deferred沒...

    Darkgel 評論0 收藏0
  • ES6-7

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

    mudiyouyou 評論0 收藏0
  • promise源碼庫

    摘要:先直接上源碼吧。阮一峰在基礎篇提到過,阮一峰基礎介紹,返回的是一個新的實例,不是原來的那個實例。同理綁定和的指向。秒后,是為了讓在隊列的最后執(zhí)行。此時將中第一個回調函數(shù)執(zhí)行的賦值給了。這就驗證了阮一峰在基礎介紹將的下面的代碼邏輯。 先直接上源碼吧。 if(!window.Promise) { function Promise(fn) { var self=thi...

    魏明 評論0 收藏0
  • JS筆記

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

    rottengeek 評論0 收藏0
  • 【前端面試分享】- 寒冬求職上

    摘要:記錄下我遇到的面試題,都有大佬分享過,附上各個大佬的文章,總結出其中的主要思想即可。推薦黑金團隊的文章前端緩存最佳實踐推薦名揚的文章淺解強緩存和協(xié)商緩存狀態(tài)碼重點是等,要給面試官介紹清楚。前言 在這互聯(lián)網(wǎng)的寒冬臘月時期,雖說過了金三銀四,但依舊在招人不斷。更偏向于招聘高級開發(fā)工程師。本人在這期間求職,去了幾家創(chuàng)業(yè),小廠,大廠廝殺了一番,也得到了自己滿意的offer。 整理一下自己還記得的面試...

    shinezejian 評論0 收藏0

發(fā)表評論

0條評論

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