深入理解Promise

編后吐槽:寫的快花眼,很詳細,耐心看必受益匪淺

JavaScript的執行環境是「單線程」的。所謂單線程,是指JS引擎中負責解釋和執行JavaScript代碼的線程只有一個,也就是一次只能完成一項任務,這個任務執行完后才能執行下一個,它會「阻塞」其他任務。這個任務可稱為主線程。但實際上還有其他線程,如事件觸發線程、ajax請求線程等。因為javascript的單線程原理,使得網絡操作,瀏覽器事件,都必須是異步執行的。

* 同步與異步

  • 同步:同步模式,即上述所說的單線程模式,一次只能執行一個任務,函數調用后需等到函數執行結束,返回執行的結果,才能進行下一個任務。如果這個任務執行的時間較長,就會導致「線程阻塞」。
var x = true;
while(x);
console.log("don‘t’ carry out");
// javascript 單線程原理導致同步執行,第三步不被執行
  • 異步:可以一起執行多個任務,函數調用后不會立即返回執行的結果,如果任務A需要等待,可先執行任務B,等到任務A結果返回后再繼續回調。
// 常見異步,定時器的使用
setTimeout(function() {
  console.log('taskA, run as asynchronous');
}, 0)
console.log('taskB, run as synchronize');
//while(true);

// taskB, run as synchronize
// taskA, run as asynchronous

?即使延時時長為0,taskA依舊晚于taskB;因為定時器是異步的,異步任務會在當前腳本所有同步任務完成之后才被執行。,如果同步任務中含有阻塞任務,即放開代碼中的while(true),那么taskA將不會被執行。

* 什么是Promise

? 古人云:“君子一諾千金”,這種“承諾某個狀態將來會執行,并且該狀態不會被改變”的對象在JavaScript中稱為Promise對象,他是一個構造函數,能將異步的操作以同步的形式表達出來,避免了嵌套地獄(層層嵌套)的發生。它供了統一的API,使得控制異步更加容易

* 回調函數

?回調函數是一段可執行的代碼段,它以「參數」的形式傳遞給其他代碼,在其合適的時間執行這段(回調函數)的代碼。
?異步可以回調,同步也可以回調;

// 同步回調
var func1 = function(cb) {
  // ..do something
  console.log("before callback");
  (cb && typeof(cb) === 'function') && cb();
  console.log("after callback");
}
var func2 = function(param) {
  // ..do something
  var start = new Date();
  if(( new Date() - start) < 3000 ){ }  // 同步實現延時函數
  console.log("I am callback");
}

func1(func2);

------output------- 
// before callback
(after 3s...)
// I am callback
// after callback

?由于是同步回調,會阻塞后面的代碼,如果func2是個死循環,后面的代碼就不執行了。為解決這個問題,我們使用異步回調,除了常見的setTimeoutajax也是應用方式之一

// 異步回調
function request(url, param, successFun, errorFunc) {
  $.ajax({
    type: 'GET',
    url: url,
    param: param,
    async: true,  // 默認為true,如果設置為false則變成同步請求
    success: successFunc,
    error: errorFunc
  });
}
request('test.html', '', function(data) {
  // 第三個參數為返回成功時執行的函數
  console.log('cbData: ', data);
}, function(data) {
  // 第四個參數為失敗時的函數
  console.log('error: ', error);
});

* 為什么要用Promise

既然已經可以實現異步回調,那我們為什么還要用Promise呢?
javascript代碼

function callback () {
  console.log("Done");
}

console.log("before setTimeout()")
setTimeout(callback, 1000);
console.log("after setTimeout()"); 

控制臺輸出

before setTimeout()
after setTimeout()
(等待一秒后...)
Done

說明: setTimeout()延時函數時javascript實現異步執行的手段之一,該例中它將callback放到等待隊列,并且開始為延時量計時,當到達延時量主線程中的事件還沒執行完成,那等待隊列中的事件繼續保持等待,直到主線程的執行完成才被加入到線程中執行。所以代碼中說的”等待一秒后”其實并不準確。

?可見,異步操作會在將來的某個時間點觸發一個函數調用。
?如果我們有這樣一個需求:下一個請求或函數必須要有上一步返回的數據才能執行,如下:

request('test1.html', '', function(data1) {
    console.log('第一次請求成功, 這是返回的數據:', data1);
    request('test2.html', data1, function (data2) {
        console.log('第二次請求成功, 這是返回的數據:', data2);
        request('test3.html', data2, function (data3) {
            console.log('第三次請求成功, 這是返回的數據:', data3);
            //request... 繼續請求
        }, function(error3) {
            console.log('第三次請求失敗, 這是失敗信息:', error3);
        });
    }, function(error2) {
        console.log('第二次請求失敗, 這是失敗信息:', error2);
    });
}, function(error1) {
    console.log('第一次請求失敗, 這是失敗信息:', error1);
});

?以上出現了多層回調嵌套,有種暈頭轉向的感覺。這也就是我們常說的厄運回調金字塔(Pyramid of Doom),編程體驗十分不好
? 不僅如此,這種代碼結構,可讀性可維護性太差,代碼復用率也很低,我們希望能將數據請求和數據處理區分開來,Promise就可以利用then進行「鏈式回調」,將異步操作以同步操作的流程表示出來。

sendRequest('test1.html', '').then(function(data1) {
    console.log('第一次請求成功, 這是返回的數據:', data1);
}).then(function(data2) {
    console.log('第二次請求成功, 這是返回的數據:', data2);
}).then(function(data3) {
    console.log('第三次請求成功, 這是返回的數據:', data3);
}).catch(function(error) {
    //用catch捕捉前面的錯誤
    console.log('sorry, 請求失敗了, 這是失敗信息:', error);
});

?Promise不僅能以同步的方式表示異步操作,還能catch到異常,這是ajax無法實現的
?

* Promise in JS

1. 基本結構

結構示意圖

ES6 規定,Promise對象是一個構造函數,用來生成Promise實例。
javascript代碼

var pm = new Promise(function(resolve, reject) {
  // ..some code
  if( /*異步操作結果*/) {
    resolve(value);
  } else {
    reject(error);
  }
})

pm.then(function(){
  // handle resolveFunc
},function() {
  // handle rejectFunc
})

這就是它的基本模型,為了繼續往下學習,我們需要補充一下Promise的一些基礎知識。

2. Promise 三種狀態

Promise鏈式調用用到resolverejectthencatch,他有以下三種狀態

  • pending - 進行中,或者等待中,表示還沒有得到結果
  • fulfilled - 已成功,在異步操作成功時調用,并將結果作為參數傳遞出去。
  • rejected - 已失敗。在異步操作失敗時調用,并將報出的錯誤作為參數傳遞出去。

?只有異步操作的結果可以決定當前是哪種狀態,其他任何操作都無法改變這個狀態,這也是Promise名字的由來。所謂的“君子一言,駟馬難追”,承諾將來某個狀態,且該狀態不會被其他因素改變。

注釋: 從基本用法的例子中我們看到Promise構造函數的參數是resolvereject,并不是三種狀態中的fulfilledrejected,原因就是:resolved表示的是已結束(已定型),它包含fullfilledrejected兩種狀態,但使用中,我們默認的將resolved當做fulfilled(成功)使用。

3. Promise 對象的特點

(1)對象的狀態不受外界因素影響。以上已經說過。
(2)一旦結果狀態生成,就不會在改變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從pending變為fulfilled和從pending變為rejected。只要這兩種狀態發生了,狀態就凝固不變,并一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這和事件監聽有很大的區別,事件監聽是實時的,如果錯過了監聽時機,就得不到要監聽的結果了

promise也有缺點
  • 無法取消Promise,一旦新建它就會立即執行,無法中途取消
  • 如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部(所以健壯的代碼要catch錯誤)
  • 當處于pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)

?

* 基本API

1 .then()

語法: Promise.prototype.then( onFulfilled, onRejected )

這是Promise最常用也是最重要的一個API,他定義了Promise的兩個回調函數,并返回一個新的Promise實例,且返回值傳入這個行Promise的resolve函數中。
因此,我們可以使用鏈式寫法,如為什么要用Promise中的最后一例,返回的還是一個Promise對象(即有異步操作),這時后一個回調函數,就會等待該Promise對象的狀態發生變化,才會被調用。

2. .catch() - 拋出異常

語法: Promise.prototype.catch( onRejected )

該方法是.then(undefined, onRejected)的別名,用于指定發生錯誤時的回調函數。

var promise = new Promise(function(resolve, reject)){
  // some code
}

promise.then(function(data)  {
  console.log('success');
}, function(error) {
  conosle.log('error', error)
})

/*---等價于---*/
promise.then(function(data){
  console.log('success');
}).catch(function(error) {
  consol;e.log('error', error)
})

再看一例

var promise = new Promise(function(resoleve, reject) {
  throw new Error('test');
});
// 等同于
var promise = new Promise(resovle, reject) {
  reject(new Error('test'));
}

// 以上的reject我們用catch來捕獲
promise.catch(function (error) {
  console.log(error);
});
---output---
Error: test

?從上例可以看出,reject方法的作用,等同于拋錯,這是Promise另一大優勢

?關于Promise拋錯有幾下幾個需要注意的地方

  1. Promise對象的錯誤,會一直向后傳遞,知道被捕獲,即錯誤總會被下一個catch所捕獲。then方法指定的回調函數,若拋出錯誤,也會被下一個catch捕獲。catch中也能拋錯,則需要后面的catch來捕獲。
 sendRequest('test.html').then(function(data1) {
    //do something
}).then(function (data2) {
    //do something
}).catch(function (error) {
    //處理前面三個Promise產生的錯誤
});
  1. promise狀態一旦改變就會凝固,不會再改變。因此promise一旦fulfilled了,再拋錯,也不會變為rejected,就不會被catch了。
var promise = new Promise(function(resolve, reject) {
  resolve();
  throw 'error';
});

promise.catch(function(e) {
   console.log(e);      //This is never called
});
  1. 如果沒有使用catch方法指定處理錯誤的回調函數,Promise對象拋出的錯誤不會傳遞到外層代碼,即不會有任何反應(Chrome會拋錯),這是Promise的另一個缺點。
var promise = new Promise(function(resolve, reject) {
  resolve();  // 狀態已經被返回為resolve
  throw 'error';
});

promise.catch(function(e) {
   console.log(e);      //This is never called
});

拋錯實例:

var p = new Promise(function(resolve, reject) {
  resolve(x);
});
p.then(function(data){
  console.log(data);
});

Chrome上的表現;


狀態為reject且控制臺報錯

? 據說只有chrome會報錯,其他瀏覽器的錯誤不會被捕獲,也不會傳遞到外層代碼,最后沒有任何輸出,promise的狀態也變為rejected

3 .all() - Promise中的“邏輯與”

? 全部執行結束且狀態均為resolve才為resolve,有一個執行錯誤,則為reject

語法: promise.all( iterable )

該方法用于將多個Promise實例,包裝成一個新的Promise實例。

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

Promise.all方法接受一個數組(或具有Iterator接口)作參數,數組中的對象(p1、p2、p3)均為promise實例(如果不是一個promise,該項會被用Promise.resolve轉換為一個promise)。它的狀態由這三個promise實例決定。

  1. 當p1, p2, p3狀態都變為fulfilled,p的狀態才會變為fulfilled,并將三個promise返回的結果,按參數的順序(而不是 resolved的順序)存入數組,傳給p的回調函數
var p1 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 3000, "first");
});
var p2 = new Promise(function(resolve, reject) {
  resolve("second");
});
var p3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 1000, "third");
});

Promise.all([p1, p2, p3]).then(function(values) {
  console.log(values);
});
 
// ----output----
// 約3秒后
// ["first", "second", "third"]
  1. 當p1, p2, p3其中之一狀態變為rejected,p的狀態也會變為rejected,并把第一個被reject的promise的返回值,立即觸發并傳給p的回調函數
// 將上例中的p2適當修改如下
var p2 = new Promise(function(resolve, reject) {
  resolve(x);
});

這時,p2會拋出錯誤,立即傳給Promise.all(),結束執行。

  1. 這多個 promise 是同時開始、并行執行的,而不是順序執行

4 .race() - 競速執行

? 需要注意的是,它并不是Promise中的“邏輯或”,而是將先結束的傳值給 then,最先執行完成的是resolveresolve,為rejectreject

語法: Promise.race( iterable )

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

var p = Promise.race([p1, p2, p3]);
  1. Promise.race方法同樣接受一個數組(或具有Iterator接口)作參數。當p1, p2, p3中有一個實例的狀態發生改變(變為fulfilled或rejected),p的狀態就跟著改變。并把第一個改變狀態的promise的返回值,傳給p的回調函數。
    執行resolve
var p1 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 500, "one"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "two"); 
});

Promise.race([p1, p2]).then(function(value) {
    console.log('resolve', value); 
}, function(error) {
    //not called
    console.log('reject', error); 
});
-------output-------
resolve two

執行reject

var p3 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "three");
});
var p4 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 100, "four"); 
});

Promise.race([p3, p4]).then(function(value) {
    //not called
    console.log('resolve', value);              
}, function(error) {
    console.log('reject', error); 
});
-------output-------
reject four
  1. 在第一個promise對象變為resolve后,并不會取消其他promise對象的執行,如下例
var fastPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log('fastPromise');
        resolve('resolve fastPromise');
    }, 100);
});
var slowPromise = new Promise(function (resolve) {
    setTimeout(function () {
        console.log('slowPromise');
        resolve('resolve slowPromise');
    }, 1000);
});
// 第一個promise變為resolve后程序停止
Promise.race([fastPromise, slowPromise]).then(function (value) {
    console.log(value);    // => resolve fastPromise
});
-------output-------
fastPromise
resolve fastPromise
slowPromise     //仍會執行

5 .resolve() - 立即執行Promise-resolve

語法: 1. Promise.resolve(value); 2. Promise.resolve(promise); 3. Promise.resolve(thenable);

它可以看做new Promise()的快捷方式。

new Promise(function (resolve) {
    resolve('Success');
});
//----等同于----
Promise.resolve('Success');
  1. 這段代碼會讓這個Promise對象立即進入resolved狀態,并將結果success傳遞給then指定的onFulfilled回調函數。由于Promise.resolve()也是返回Promise對象,因此可以用.then()處理其返回值。
Promise.resolve('success').then(function (value) {
    console.log(value);
});
-------output-------
Success
//Resolving an array
Promise.resolve([1,2,3]).then(function(value) {
  console.log(value[0]);    // => 1
});

//Resolving a Promise
var p1 = Promise.resolve('this is p1');
var p2 = Promise.resolve(p1);
p2.then(function (value) {
    console.log(value);     // => this is p1
});
  1. Promise.resolve()的另一個作用就是將thenable對象(即帶有then方法的對象)轉換為Promise對象。
var p1 = Promise.resolve({ 
    then: function (resolve, reject) { 
        resolve("this is an thenable object!");
    }
});
console.log(p1 instanceof Promise);     // => true

p1.then(function(value) {
    console.log(value);     // => this is an thenable object!
  }, function(e) {
    //not called
});
  1. 無論是在什么時候拋異常,只要promise狀態變成resolved或rejected,狀態不會再改變,這和新建promise是一樣的。
//在回調函數前拋異常
var p1 = { 
    then: function(resolve) {
      throw new Error("error");
      resolve("Resolved");
    }
};

var p2 = Promise.resolve(p1);
p2.then(function(value) {
    //not called
}, function(error) {
    console.log(error);       // => Error: error
});
//在回調函數后拋異常
var p3 = { 
    then: function(resolve) {
        resolve("Resolved");
        throw new Error("error");
    }
};

var p4 = Promise.resolve(p3);
p4.then(function(value) {
    console.log(value);     // => Resolved
}, function(error) {
    //not called
});
6 .reject() - 立即執行Promise-reject

語法:Promise.reject(reason)

和上述的Promise.resolve()類似,它也是new Promise()的快捷方式。

Promise.reject(new Error('error'));

/*******等同于*******/
new Promise(function (resolve, reject) {
    reject(new Error('error'));
});

這段代碼會讓這個Promise對象立即進入rejected狀態,并將錯誤對象傳遞給then指定的onRejected回調函數。

?

* Promise 幾個應用

?Promise中的.then方法,可以接收構造函數中處理的狀態的變化,并且分別對應執行。.then有兩個函數參數,分別接收resolvedrejected的執行。
?簡單來說, then就是定義resolvereject函數的,其resolve函數相當于:

function resolveFun(data) {
  // data 為 promise中resolve函數中所帶的參數
}

?Promise新建后就會立即執行。而then方法中指定的回調函數,將在當前腳本所有同步任務執行完才會執行。如下例:

1. 執行順序

javascript代碼

var promise = new Promise(function(resolve, reject) {
  console.log('before resolved');
  resolve();
  console.log('after resolved');
});

promise.then(function() {
  console.log('resolved');
});

console.log('outer');

-------output-------
// before resolved
// after resolved
// outer
// resolved
2.調用延時執行函數
function timeout (ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'done');   // 三個參數?往下看
  })
}

timeout(100).then((value) => {
  console.log(value)
})

// 輸出 done

?該例寫了一個延時調用函數,設置ms時間以后,才將Promise的狀態修改為resolve,然后執行.then中的打印操作。
?這里埋了個點,比較有意思。當給setTimeout()傳入大于兩個參數時,從第三個開始代表的是傳給延時執行函數的參數,想具體了解的可以戳這里

3. 異步加載圖片
function loadImgAsync (url) {
  return new Promise(function (resolve, reject) {
    var img = new Image();
    
    image.onload = function () {
      resolve(image);
    };
    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    }
    image.src = url;
  })
}

?上訴的例子中,先加載圖片,如果圖片加載成功,就調用resolve,否則調用reject

4. 用Promise實現一個Ajax操作
var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';

// 封裝一個get請求的方法
function getJSON(url) {
    return new Promise(function(resolve, reject) {
        var XHR = new XMLHttpRequest();
        XHR.open('GET', url, true);
        XHR.send();

        XHR.onreadystatechange = function() {
            if (XHR.readyState == 4) {
                if (XHR.status == 200) {
                    try {
                        var response = JSON.parse(XHR.responseText);
                        resolve(response);
                    } catch (e) {
                        reject(e);
                    }
                } else {
                    reject(new Error(XHR.statusText));
                }
            }
        }
    })
}

getJSON(url).then(resp => console.log(resp));

?為了健壯性,處理了很多可能出現的異常,總之,成功了就resolve,失敗了就reject。
?如果調用resolve函數和reject函數時帶有參數,那么它們的參數會被傳遞給回調函數。reject函數的參數通常是Error對象的實例,表示拋出的錯誤;resolve函數的參數除了正常的值以外,還可能是另一個 Promise 實例

現在所有的庫幾乎都將ajax請求利用Promise進行了封裝,因此我們在使用jQuery等庫中的ajax請求時,都可以利用Promise來讓我們的代碼更加優雅和簡單。這也是Promise最常用的一個場景

5. resolve參數為另一個Promise實例 - 難點
var p1 = new Promise(function(resolve, reject) {
  // ..some code
})
var p2 = new Promise(function(resolve, reject){
  // ..some code
  resolve(p1)
})

? 上述中,p1和p2都是一個實例,定義了之后都立即執行,但是p2的resolve方法將p1作為參數,即一個異步操作的結果是返回另一個異步操作
?注意:這時,p1的狀態決定了p2的狀態,如果p1的狀態是pending,那么,p2的回調函數就會等待p1狀態的改變,如果p1的狀態已經是resolve或rejected,那么p2就會立即被執行。

很典型的一個例子

var p1 = new Promise(function(resolve, reject){
  setTimeout(() => reject(new Error('Fail')), 3000)
})
var p2 = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})

p2.then(result => conosle.log(result))
.catch(error => console.log(error)) 
//  建議大家看一下瀏覽器輸出效果
// 輸出 Error: fail

分析
?上述代碼中,p1和p2被定義后都立即異步執行,p1執行3秒之后返回reject,p2執行1秒后返回resolve,但p2的返回結果是p1,所以,又要等待2秒p1返回結果,即p2的返回結果是“p1的返回結果”,這樣大家就明白了,最終執行的其實是p1的結果reject,所以p2這里調用.catch而不是.then

?注意:調用resolvereject并不會終結Promise的參數函數的執行。

new Promise(function(resolve, reject) {
  resolve(1);
  console.log(2);
}).then(val => {
  console.log(val)
}) 

//  2
//  1

?上面代碼中,調用resolve(1)以后,后面的console.log(2)還是會執行,并且會首先打印出來。這是因為立即 resolved 的 Promise 是在本輪事件循環的末尾執行,總是晚于本輪循環的同步任務。雖然有這種策略,但我們一般習慣還是將其放在最后。

6. 聊天系統獲取兩個用戶的信息 - Promise.all()
var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
// 同時執行p1和p2,并在它們都完成后執行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 獲得一個Array: ['P1', 'P2']
});
7. 多個異步任務提高容錯率 - Promise.race()
var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

由于p1執行較快,Promise的then()將獲得結果'P1'。p2仍在繼續執行,但執行結果將被丟棄。

* Promise常見問題

到這里,相信你已學會使用Promise了,congratulation!

1. reject 和 catch 的區別

  • promise.then(onFulfilled, onRejected)
    onFulfilled中發生異常的話,在onRejected中是捕獲不到這個異常的。
  • promise.then(onFulfilled).catch(onRejected)
    .then中產生的異常能在.catch中捕獲

綜上所述,建議使用第二種,因為能捕獲之前的所有異常。當然了,第二種的.catch()也可以使用.then()的捕錯法表示,它們本質上是沒有區別的,.catch === .then(null, onRejected)

2. 如果在then中拋錯,而沒有對錯誤進行處理(即catch),那么會一直保持reject狀態,直到catch了錯誤
/* 例4.1 */
function taskA() {
    console.log(x);
    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)    // 拋出錯誤,不繼續Task A”
    .then(taskB)   // .then沒有捕獲A拋出的錯,不打印 “Task B”
    .catch(onRejected)  // 捕獲了A的錯,打印錯誤信息
    .then(finalTask);   // 錯誤已經被捕獲,執行resolve
    
-------output-------
Catch Error: A or B,ReferenceError: x is not defined
Final Task

來看一下該例的流程


很明顯,A拋錯時,會按照taskA → onRejected → finalTask這個流程來處理。A拋錯后,若沒有對它進行處理,狀態就會維持rejected,taskB不會執行,直到catch了錯誤。

3. 每次調用then都會返回一個新創建的promise對象,而then內部只是返回的數據
//方法1:對同一個promise對象同時調用 then 方法
var p1 = new Promise(function (resolve) {
    resolve(100);
});
p1.then(function (value) {
    return value * 2;
});
p1.then(function (value) {
    return value * 2;
});
p1.then(function (value) {
    console.log("finally: " + value);
});
-------output-------
finally: 100

then的調用幾乎是同時開始執行的,且傳給每個then的value都是100,這種方法應當避免。正確的應該是采用鏈式調用。

//方法2:對 then 進行 promise chain 方式進行調用
var p2 = new Promise(function (resolve) {
    resolve(100);
});
p2.then(function (value) {
    return value * 2;
}).then(function (value) {
    return value * 2;
}).then(function (value) {
    console.log("finally: " + value);
});
-------output-------
finally: 400

或許上面這個案例你還沒感覺,來看看這個:

function badAsyncCall(data) {
    var promise = Promise.resolve(data);
    promise.then(function(value) {
        //do something
        return value + 1;
    });
    return promise;
}
badAsyncCall(10).then(function(value) {
   console.log(value);          //想要得到11,實際輸出10
});
-------output-------
10

正確的寫法應該是:

function goodAsyncCall(data) {
    var promise = Promise.resolve(data);
    return promise.then(function(value) {
        //do something
        return value + 1;
    });
    return promise;
}
goodAsyncCall(10).then(function(value) {
   console.log(value);
});
-------output-------
11
4. 在異步回調中拋錯,不會被catch到
// Errors thrown inside asynchronous functions will act like uncaught errors
var promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw 'Uncaught Exception!';
  }, 1000);
});

promise.catch(function(e) {
  console.log(e);       //This is never called
});
5. promise狀態變為resove或reject,就凝固了,不會再改變
console.log(1);
new Promise(function (resolve, reject){
    reject();
    setTimeout(function (){
        resolve();            //not called
    }, 0);
}).then(function(){
    console.log(2);
}, function(){
    console.log(3);
});
console.log(4);

-------output-------
1
4
3

花了兩天工作之余的時間,總算寫完了~~看了很多資源,借鑒了許多觀點和例子,希望能幫到大家
參考資源:
?阮一峰的ES6 教程
?廖雪峰的官方網站
?Promise迷你書
?MDN Promise
廖雪峰的官網能直接在里面自定義代碼嘗試運行,挺有意思。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Promiese 簡單說就是一個容器,里面保存著某個未來才會結束的事件(通常是一個異步操作)的結果,語法上說,Pr...
    雨飛飛雨閱讀 3,380評論 0 19
  • 00、前言Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區...
    夜幕小草閱讀 2,139評論 0 12
  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和...
    呼呼哥閱讀 2,189評論 0 16
  • 本文適用的讀者 本文寫給有一定Promise使用經驗的人,如果你還沒有使用過Promise,這篇文章可能不適合你,...
    HZ充電大喵閱讀 7,333評論 6 19
  • 海洋上浪花飛濺 鷗鳥飛翔 你從他方駛來 與我相遇 孤獨的行程 都已走了很遠 此刻的相遇 一掃了往日的寂寞 也許我們...
    昨日的酒閱讀 253評論 0 4