1. 程式人生 > >promise基礎

promise基礎

.com 不想 fun finall all 這也 版本 避免 .proto

Promise 是異步編程的一種解決方案,主要解決了異步回調嵌套寫法的問題,還提供了統一的接口,使得控制異步操作更加容易。

function asyncFn(){
    return new Promise(function(resove, reject){
        /*setTimeout(function(){
            resove(‘async function‘);
        },100);*/
        reject(new Error(‘error test‘)) //同一時間promise狀態是單一的,要麽是reject,要麽是resove
resove(‘async function‘); //不會執行 }); } //Promise 新建後就會立即執行。 var promise = new Promise(function(resolve, reject) { console.log(‘Promise‘); //立即執行 resolve(); }); promise.then(function() { console.log(‘Resolved.‘); }); console.log(‘Hi!‘); // Promise // Hi! // Resolved
//異步加載圖片的例子。 function loadImageAsync(url) { return new Promise(function(resolve, reject) { var image = new Image(); image.onload = function() { resolve(image); }; image.onerror = function() { reject(new Error(‘Could not load image at ‘ + url)); }; image.src
= url; }); } //resolve函數的參數除了正常的值以外,還可能是另一個 Promise 實例,比如像下面這樣 var p1 = new Promise(function (resolve, reject) { // ... }); var p2 = new Promise(function (resolve, reject) { // ... resolve(p1); }) //這時p1的狀態就會傳遞給p2,也就是說,p1的狀態決定了p2的狀態。如果p1的狀態是Pending,那麽p2的回調函數就會等待p1的狀態改變;如果p1的狀態已經是Resolved或者Rejected,那麽p2的回調函數將會立刻執行。 //一般來說,調用resolve或reject以後,Promise 的使命就完成了,後繼操作應該放到then方法裏面,而不應該直接寫在resolve或reject的後面。所以,最好在它們前面加上return語句 new Promise((resolve, reject) => { return resolve(1); // 後面的語句不會執行 console.log(2); }) //Promise 實例具有then方法,它的作用是為 Promise 實例添加狀態改變時的回調函數,then方法返回的是一個新的Promise實例 getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { // ... }); //上面的代碼使用then方法,依次指定了兩個回調函數。第一個回調函數完成以後,會將返回結果作為參數,傳入第二個回調函數。第二個回調函數會等待第一個回調函數執行完根據返回新的promise對象狀態執行相應的步驟 asyncFn().then(function(value){ console.log(value); }).catch(function(error){ //一般說來,使用.catch來將resolve和reject處理分開來寫是比較推薦的做法 console.log(error) }) //.catch 等價 promise.then(undefined, onRejected) /* asyncFn().then(function(value){ console.log(value); }, function(error){ console.log(error) })*/ /*new Promise(function(resove, reject){ resove(123); })*/ Promise.resolve(132).then(function(value){ console.log(value) //即使在調用 promise.then 註冊回調函數的時候promise對象已經是確定的狀態,Promise也會以異步的方式調用該回調函數 }) //Promise.resolve靜態方法除了是快捷方式外,還可以把thenable對象轉換為promise對象 Promise.reject(new Error(‘error test2‘)).catch(function(error){ console.log(error); }) //Promise.reject()方法的參數,會原封不動地作為reject的理由,變成後續方法的參數。這一點與Promise.resolve方法不一致。 //Promise保證了每次調用都是以異步方式進行的 //Promise#then 不僅僅是註冊一個回調函數那麽簡單,它還會將回調函數的返回值進行變換,創建並返回一個promise對象。 //實際上 Promise#catch 只是 promise.then(undefined, onRejected); 方法的一個別名而已 //在IE8及以下版本基於ECMAScript 3實現的,在ECMAScript 3中保留字是不能作為對象的屬性名使用的(點標記法(dot notation) 要求對象的屬性必須是有效的標識符),因此不能將 catch 作為屬性來使用,也就不能編寫類似 promise.catch() 的代碼,因此就出現了 identifier not found 這種語法錯誤了 //而現在的瀏覽器都是基於ECMAScript 5的,而在ECMAScript 5中保留字都屬於 IdentifierName ,也可以作為屬性名使用了 //但是使用 中括號標記法(bracket notation)的話,則可以將非合法標識符作為對象的屬性名使用 var promise = Promise.reject(new Error("message")); //reject方法的作用,等同於拋出錯誤 promise["catch"](function (error) { console.error(error); }); //或者我們不單純的使用 catch ,而是使用 then 也是可以避免這個問題的 //次調用then都會返回一個新創建的promise對象 //Promise.all 接收一個 promise對象的數組作為參數,當這個數組裏的所有promise對象全部變為resolve或reject狀態的時候,它才會去調用 .then 方法, 調用then方法values狀態值也是一個數組 //傳遞給 Promise.all 的promise並不是一個個的順序執行的,而是同時開始、並行執行的。 // `delay`毫秒後執行resolve function timerPromisefy(delay) { return new Promise(function (resolve) { setTimeout(function () { resolve(delay); }, delay); }); } var startDate = Date.now(); // 所有promise變為resolve後程序退出 Promise.all([ timerPromisefy(1), timerPromisefy(32), timerPromisefy(64), timerPromisefy(128) ]).then(function (values) { console.log(Date.now() - startDate + ‘ms‘); // 約128ms console.log(values); // [1,32,64,128] 狀態值 }); var p = Promise.all([p1, p2, p3]); //只有p1、p2、p3的狀態都變成fulfilled,p的狀態才會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。 //只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。 //如果作為參數的 Promise 實例,自己定義了catch方法,那麽它一旦被rejected,並不會觸發Promise.all()的catch方法。,因為自己定義了catch方法會返回新的promise對象,也會變成resolved //Promise.race 只要有一個promise對象進入 FulFilled 或者 Rejected 狀態的話,就會繼續進行後面的then或catch處理。 var p = Promise.race([p1, p2, p3]); //只要p1、p2、p3之中有一個實例率先改變狀態,p的狀態就跟著改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數 //Promise.race方法的參數與Promise.all方法一樣,如果不是 Promise 實例,就會先調用Promise.resolve方法,將參數轉為 Promise 實例,再進一步處理。 const p = Promise.race([ fetch(‘/resource-that-may-take-a-while‘), new Promise(function (resolve, reject) { setTimeout(() => reject(new Error(‘request timeout‘)), 5000) }) ]); p.then(response => console.log(response)); p.catch(error => console.log(error)); //上面代碼中,如果5秒之內fetch方法無法返回結果,變量p的狀態就會變為rejected,從而觸發catch方法指定的回調函數。 //需要註意的是,立即resolve的Promise對象,是在本輪“事件循環”(event loop)的結束時,而不是在下一輪“事件循環”的開始時。 var winnerPromise = new Promise(function (resolve, reject) { setTimeout(function () { console.log(‘this is winner log‘); //reject(new Error(‘this is winner error‘)); resolve(‘this is winner‘); }, 4); }); var loserPromise = new Promise(function (resolve, reject) { setTimeout(function () { console.log(‘this is loser log‘); reject(new Error(‘this is loser error‘)); // resolve(‘this is loser‘); }, 1000); }); // 第一個promise變為resolve後程序停止 Promise.race([winnerPromise, loserPromise]).then(function (value) { console.log(value); // => ‘this is winner‘ }).catch(function(error){ console.log(‘error:‘+error) }); // Promise.race 在第一個promise對象變為Fulfilled或Rejected之後,並不會取消其他promise對象的執行,但是其他promise對象執行後不會觸發then或catch。 // then or catch? // .catch 也可以理解為 promise.then(undefined, onRejected) //Promise.resolve(42).then(throwError, onRejected); 有異常發生onRejected不會被調用 //Promise.resolve(42).then(throwError).catch(onRejected); 有異常發生時onRejected會被調用 //.then 方法中的onRejected參數所指定的回調函數,實際上針對的是其promise對象或者之前的promise對象,而不是針對 .then 方法裏面指定的第一個參數,即onFulfilled所指向的對象,這也是 then 和 catch 表現不同的原因。 //Deferred 擁有 Promise //Deferred 具備對 Promise的狀態進行操作的特權方法 function Deferred() { this.promise = new Promise(function (resolve, reject) { this._resolve = resolve; this._reject = reject; }.bind(this)); } Deferred.prototype.resolve = function (value) { this._resolve.call(this.promise, value); }; Deferred.prototype.reject = function (reason) { this._reject.call(this.promise, reason); }; //Promise一般都會在構造函數中編寫主要處理邏輯,對 resolve、reject 方法的調用時機也基本是很確定的。 new Promise(function (resolve, reject){ // 在這裏進行promise對象的狀態確定 }); //Deferred的話,並不需要將處理邏輯寫成一大塊代碼,只需要先創建deferred對象,可以在任何時機對 resolve、reject 方法進行調用。 var deferred = new Deferred(); // 可以在隨意的時機對 `resolve`、`reject` 方法進行調用 //Promise對象的回調鏈,不管以then方法或catch方法結尾,要是最後一個方法拋出錯誤,都有可能無法捕捉到(因為Promise內部的錯誤不會冒泡到全局)。因此,我們可以提供一個done方法,總是處於回調鏈的尾端,保證拋出任何可能出現的錯誤。 Promise.prototype.done = function (onFulfilled, onRejected) { this.then(onFulfilled, onRejected) .catch(function (reason) { // 拋出一個全局錯誤 setTimeout(() => { throw reason }, 0); }); }; //不管怎樣,done都會捕捉到任何可能出現的錯誤,並向全局拋出。 //finally方法用於指定不管Promise對象最後狀態如何,都會執行的操作。它與done方法的最大區別,它接受一個普通的回調函數作為參數,該函數不管怎樣都必須執行。 Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); }; //不管前面的Promise是fulfilled還是rejected,都會執行回調函數callback //經常遇到一種情況:不知道或者不想區分,函數f是同步函數還是異步操作,但是想用 Promise 來處理它。因為這樣就可以不管f是否包含異步操作,都用then方法指定下一步流程,用catch方法處理f拋出的錯誤 Promise.try(database.users.get({id: userId})) .then(...) .catch(...)
//目前只是提案

參考文檔:

http://liubin.org/promises-book/

https://github.com/ruanyf/es6tutorial/blob/gh-pages/docs/promise.md

promise基礎