setTimeout/EventEmitter/Promise/generator/async-await 今天着重來說解一下 promise數組
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);
doSomething().then(function () { return doSomethingElse(); }); doSomething().then(function () { doSomethingElse(); }); doSomething().then(doSomethingElse()); doSomething().then(doSomethingElse);
1.new promise (resolve,reject){};必定要resolve,或者reject 出去不要,作反作用函數。 2.then 操做裏面 接收 promise 對象,若是在promise裏面是執行的純函數,也能夠返回一個 promise。resolve("常量")。避免promise 穿透
新手錯誤 #1: promise版的金字塔問題
觀察你們如何使用 PouchDB 這類大型的 promise 風格的API,我發現大量錯誤的 promise 使用形式。最多見的錯誤就是下面這個:promise
remotedb.allDocs({ include_docs: true, attachments: true }).then(function (result) { var docs = result.rows; docs.forEach(function(element) { localdb.put(element.doc).then(function(response) { alert("Pulled doc with id " + element.doc._id + " and added to local db."); }).catch(function (err) { if (err.status == 409) { localdb.get(element.doc._id).then(function (resp) { localdb.remove(resp._id, resp._rev).then(function (resp) {
// et cetera...
是的,實際上你能夠像使用回調同樣使用 promises,恩,就像用打磨機去削腳趾甲同樣,你確實能夠這麼作。緩存
而且若是你覺得這樣的錯誤只限於初學者,那麼你會驚訝於我其實是在黑莓官方開發者博客上看到上面的代碼。老的回調風格的習慣難以消滅。(至開發者: 抱歉選了你的例子,可是你的例子將會有積極的教育意義)異步
正確的風格應該是這樣:async
remotedb.allDocs(...).then(function (resultOfAllDocs) { return localdb.put(...); }).then(function (resultOfPut) { return localdb.get(...); }).then(function (resultOfGet) { return localdb.put(...); }).catch(function (err) { console.log(err); });
這種寫法被稱爲 composing promises ,是 promises 的強大能力之一。每個函數只會在前一個 promise 被調用而且完成回調後調用,而且這個函數會被前一個 promise 的輸出調用,稍後咱們在這塊作更多的討論。ide
下面的代碼有什麼問題?函數
somePromise().then(function () { someOtherPromise(); }).then(function () { // Gee, I hope someOtherPromise() has resolved! // Spoiler alert: it hasn't. });
好了,如今是時候討論一下關於 promises 你所須要知道的一切。code
認真的說,這是一個一旦你理解了它,就會避免全部我說起的錯誤的古怪的技巧。你準備好了麼?對象
就如我前面所說,promises 的奇妙在於給予咱們之前的 return 與 throw。可是在實踐中這究竟是怎麼一回事呢?blog
每個 promise 都會提供給你一個 then() 函數 (或是 catch(),實際上只是 then(null, ...) 的語法糖)。當咱們在 then() 函數內部時:
somePromise().then(function () { // I'm inside a then() function! });
咱們能夠作什麼呢?有三種事情:
return 另外一個 promise
return 一個同步的值 (或者 undefined)
throw 一個同步異常
就是這樣。一旦你理解了這個技巧,你就理解了 promises。所以讓咱們逐個瞭解下。
返回另外一個 promise
這是一個在 promise 文檔中常見的使用模式,也就是咱們在上文中提到的 「composing promises」:
getUserByName('nolan').then(function (user) { return getUserAccountById(user.id); }).then(function (userAccount) { // I got a user account! });
注意到我是 return
第二個 promise,這個 return
很是重要。若是我沒有寫 return
,getUserAccountById()
就會成爲一個反作用,而且下一個函數將會接收到 undefined
而非 userAccount
。
返回一個同步值 (或者 undefined)
返回 undefined 一般是錯誤的,可是返回一個同步值其實是將同步代碼包裹爲 promise 風格代碼的一種很是讚的手段。舉例來講,咱們對 users 信息有一個內存緩存。咱們能夠這樣作:
getUserByName('nolan').then(function (user) { if (inMemoryCache[user.id]) { return inMemoryCache[user.id]; // returning a synchronous value! } return getUserAccountById(user.id); // returning a promise! }).then(function (userAccount) { // I got a user account! });
是否是很贊?第二個函數不須要關心 userAccount 是從同步方法仍是異步方法中獲取的,而且第一個函數能夠很是自由的返回一個同步或者異步值。
不幸的是,有一個不便的現實是在 JavaScript 中無返回值函數在技術上是返回 undefined,這就意味着當你本意是返回某些值時,你很容易會不經意間引入反作用。
出於這個緣由,我我的養成了在 then() 函數內部 永遠返回或拋出 的習慣。我建議你也這樣作。
拋出同步異常
談到 throw,這是讓 promises 更加讚的一點。好比咱們但願在用戶已經登出時,拋出一個同步異常。這會很是簡單:
getUserByName('nolan').then(function (user) { if (user.isLoggedOut()) { throw new Error('user logged out!'); // throwing a synchronous error! } if (inMemoryCache[user.id]) { return inMemoryCache[user.id]; // returning a synchronous value! } return getUserAccountById(user.id); // returning a promise! }).then(function (userAccount) { // I got a user account! }).catch(function (err) { // Boo, I got an error! });
若是用戶已經登出,咱們的 catch() 會接收到一個同步異常,而且若是 後續的 promise 中出現異步異常,他也會接收到。再強調一次,這個函數並不須要關心這個異常是同步仍是異步返回的。
這種特性很是有用,所以它可以在開發過程當中幫助定位代碼問題。舉例來講,若是在 then() 函數內部中的任何地方,咱們執行 JSON.parse(),若是 JSON 格式是錯誤的,那麼它就會拋出一個異常。若是是使用回調風格,這個錯誤極可能就會被吃掉,可是使用 promises,咱們能夠輕易的在 catch() 函數中處理它了。
咱們能夠作什麼呢?有三種事情:
return 另外一個 promise
return 一個同步的值 (或者 undefined)
throw 一個同步異常