Promise 對象初嘗試

promise-1.png

瀏覽器支持

promise-3.jpg
http://caniuse.com/promises/embed/agents=desktopjavascript

What is Promise?

Promise是抽象異步處理對象以及對其進行各類操做的組件。html

說到 javascript 異步操做,可能想到的是這樣:java

// 以 jQuery 的 ajax 爲例
$.get('/get_url', function(result, status) {
    if(status == 'success') {
        alert('success');
    }
    if(status == 'error') {
        alert('error');
    }
});

對於 ajax 的 get 操做來講,是一個異步的過程,經過回調函數,在獲得返回的時候纔會去執行操做。ajax

可是試想一下當操做愈來愈多,回調裏面還要回調的時候,一層層回調函數是否是讓人抓狂,不論在代碼可讀性仍是編寫效率來看都是很麻煩的。
看一下咱們用 Promise 能夠怎麼作一個異步的操做:數組

// 這個 getData 是咱們預先實例好的一個 Promise 對象,如何處理這個對象咱們這裏不討論
var promise = getData('/get_url');![圖片描述][3]
promise.then(function(result) {
    console.log(result);
}).catch(function(error) {
  console.log(error);
});

這樣的風格是否是會更好呢,執行一個 promise,而後 then 裏面傳入回調函數,若是願意,咱們能夠在 then 後面再更不少個 then,catch 能夠捕捉錯誤,看起來代碼清晰簡明多了。
因此,promise的功能是能夠將複雜的異步處理輕鬆地進行模式化。promise

構造函數 Constructor

new Promise(executor);
new Promise(function(resolve, reject) { ... });

這裏的 executor 是咱們實例化一個 promise 對象時應該傳入的參數,這個參數只一個函數,這個函數接受兩個參數 resolvereject
兩個方法:瀏覽器

  • resolve(result) 在 promise 中執行這個方法表示成功,會在執行以後執行後面的 then 所傳入的函數,它接受到的參數也會被 then 裏面的函數接受到,通常來講參數爲執行結果成功時候的數據;併發

  • reject(error) 在 promise 中執行這個方法表示失敗,他通常接受一個 error 錯誤參數,會被後面的 catch 捕捉到錯誤執行。
    demo:異步

var testFoo = function() {
    return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('success');
    }, 2000);
  });
};
testFoo().then(function(result) {
  console.log(result);
}).catch(function(error) {
  console.log(error);
});

在這裏咱們定義了一個 testFoo 函數,這個函數返回一個Promise的實例化對象,兩秒以後會執行resolve('success');,表示成功,傳入一個參數,在兩秒以後,咱們then裏面傳入的函數執行了,接收到了剛剛那個參數;可是catch裏面的函數並無執行,由於咱們沒有在 promise 裏面執行拒絕操做。函數

若是咱們在四秒以後執行 reject 操做呢:

var testFoo = function() {
    return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('success');
    }, 2000);
    setTimeout(function() {
      reject('error');
    }, 4000);
  });
};
testFoo().then(function(result) {
  console.log(result);
}).catch(function(error) {
  console.log(error);
});

貌似只出現resolve的結果,由於一個 promise 沒辦法作屢次結果操做。
咱們就這樣:

var testFoo = function() {
    return new Promise(function(resolve, reject) {
    setTimeout(function() {
      reject('error');
    }, 4000);
  });
};
testFoo().then(function(result) {
  console.log(result);
}).catch(function(error) {
  console.log(error);
});

如今結果如咱們所預料了。

PromiseStatus 狀態

狀態分爲三種:

  • fulfilled - Fulfilled 已完成,在 resolve時,調用 then 的 onFulfilled函數;

  • Rejected - Rejected 拒絕,在reject時,調用 then 的 onRejected函數,或者 catch 裏面的函數;

  • unresolved - Pending 等待,是 promise 初始化的時候的狀態。

promise 的 初始化狀態爲 unresolved,根據異步結果變爲 fulfilled 或者 Rejected,一旦變爲其中一個就不可改變,這也是咱們以前上面爲何執行了 resolve 以後再執行 reject 而沒有結果的緣由了。

方法概述

快捷方法

  • Promise.resolve() 這個是promise的靜態方法

Promise.resolve(10).then(function(value){
    console.log(value);
});

這個方法會讓 Promise 當即進入 fulfilled 狀態,通常用來測試用。

  • Promise.reject()相應着咱們有這個方法

Promise.reject('err').catch(function(err){
    console.log(err);
});

實例方法

  • then(onFulfilled, onRejected)這個方法具體是這樣的,傳入兩個函數,一個處理fulfilled狀態,另外一個處理Rejected狀態,通常使用咱們只傳入第一個函數,第二個放在 catch 來處理。

  • catch(onRejected)處理Rejected狀態,能夠這麼理解catch(onRejected)=promise.then(undefined, onRejected)

鏈式調用

看看這個 demo:

var testFoo = function() {
    return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(1);
    }, 2000);
  });
};
testFoo().then(function(result) {
  console.log(result);
  return ++result;
}).then(function(result) {
  console.log(result);
  return ++result;
}).then(function(result) {
  console.log(result);
  return ++result;
}).catch(function(error) {
  console.log(error);
});

// 1
// 2
// 3

能夠看見結果,這個方法的流程是什麼樣的呢?
promise-2.png
第一個 then 函數和以前講的同樣,處理 promise 的 fulfilled,第二個 then 的函數是處理前一個 then 函數處理完的結果,他們之間參數傳遞的途徑是前一個 then 函數 return 一個數據,而後後一個 then 函數接受到這個參數。
若是你願意,能夠再後面加不少不少個 then,他們的流程主要是這樣。

若是咱們把 catch 提早呢?

var testFoo = function() {
    return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(1);
    }, 2000);
  });
};
testFoo().then(function(result) {
  console.log(result);
  return ++result;
}).then(function(result) {
  console.log(result);
  return ++result;
}).catch(function(error) {
  console.log(error);
}).then(function(result) {
  console.log(result);
  return ++result;
});

// 1
// 2
// 3

能夠看出,結果同樣,說明catch只會在發生 reject 的時候調用。
那若是在中間的一個 then 中拋出一個異常呢?

var testFoo = function() {
    return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(1);
    }, 2000);
  });
};
testFoo().then(function(result) {
  console.log(result);
  return ++result;
}).then(function(result) {
  console.log(result);
  throw new Error("throw Error")
  return ++result;
}).then(function(result) {
  console.log(result);
  return ++result;
}).catch(function(error) {
  console.log(error);
});
// 1
// 2
// Error: throw Error

咱們在第二個then中拋出一個異常,然後當即被 catch 捕捉到,第三個 then 並無執行。

到這裏咱們想一下從then的傳參和捕捉異常來看,新加一個 then 只是註冊了一個回調函數那麼簡單嗎?
不不不,每次調用then都會返回一個新建立的promise對象,這就解釋了上面的一切緣由。

併發調用

試想一個場景,咱們執行多個異步操做(ajax等等),可是咱們想在這幾個操做都完成的時候纔去執行一個函數。
若是按照傳統的方法來作,代碼會很亂很散,關鍵不優雅。讓咱們嘗試用 promise 。
先看這個 demo

var testFoo = function(time, value) {
    return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(value);
    }, time * 1000);
  });
};
var tasks = {
  task1: function() {
    return testFoo(1, 2);
  },
  task2: function() {
    return testFoo(1.3, 3);
  },
  task3: function() {
    return testFoo(1.5, 1);
  }
};
var main = function() {
  function recordValue(results, value) {
    results.push(value);
    console.log(value);
    console.log(results);
    return results;
  }
  var pushValue = recordValue.bind(null, []);
  return tasks.task1().then(pushValue).then(tasks.task2).then(pushValue).then(tasks.task3).then(pushValue);
};
main().then(function(value) {
  console.log(value);
});

// [2, 3, 1]

這麼實現明顯看起來凌亂,特別對於 main 函數,可讀性也比較差。
那麼有沒有更優雅的方法呢,答案:有!?。

Promise.all

Promise.all 方法接受一個以 promise 對象爲元素的數組,在所有執行操做完成後纔回調用then裏面的方法,看代碼:

var testFoo = function(time, value) {
    return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(value);
    }, time * 1000);
  });
};
var tasks = {
  task1: function() {
    return testFoo(1, 2);
  },
  task2: function() {
    return testFoo(1.3, 3);
  },
  task3: function() {
    return testFoo(1.5, 1);
  }
};
var main = function() {
  return Promise.all([tasks.task1(), tasks.task2(), tasks.task3()]);
}
main().then(function(result) {
  console.log(result);
});

// [2, 3, 1]

咱們吧要執行的 promise 對象做爲數組的元素傳給 Promise.all()Promise.all().then()裏面定義的函數接受到一個數組,元素是這幾個操做返回的值,順序和 promise 對象放入的順序同樣,好比第一個 promise 對象返回的值是2,那麼結果的第一個元素就是2。
而且每個promise是同時執行的--併發。
在全部 promise 的狀態爲 FulFilled 的時候纔會去執行 then 裏面的函數。
那麼捕捉異常呢?
修改一下例子:

var testFoo = function(time, value, err) {
    return new Promise(function(resolve, reject) {
    setTimeout(function() {
      if(err) {
        reject(err);
        return false;
      }
      resolve(value);
    }, time * 1000);
  });
};
var tasks = {
  task1: function() {
    return testFoo(1, 2, 'error');
  },
  task2: function() {
    return testFoo(1.3, 3, 'error1');
  },
  task3: function() {
    return testFoo(1.5, 1);
  }
};
var main = function() {
  return Promise.all([tasks.task1(), tasks.task2(), tasks.task3()]);
}
main().then(function(result) {
  console.log(result);
}).catch(function(err) {
  console.log(err);
});

// [2, 3, 1]

咱們讓其中2 promise 個拋出異常,看到捕捉到了那個異常,並且是捕捉到第一個異常就中止了。
說明 all 的操做是當全部 promise 狀態爲 FulFilled 的時候纔會執行 then 的操做。而一旦有一個 Rejected 就catch這個異常,而且中止。

Promise.race

他和 Promise.all 同樣,接受一個 promise 對象組成的數組,也是併發執行,可是 Promise.race 是隻要有一個promise對象進入 FulFilled 或者 Rejected 狀態的話,就會繼續進行後面的處理。

var testFoo = function(time, value) {
    return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(value);
    }, time * 1000);
  });
};
var tasks = {
  task1: function() {
    return testFoo(1, 2);
  },
  task2: function() {
    return testFoo(1.3, 3);
  },
  task3: function() {
    return testFoo(1.5, 1);
  }
};
var main = function() {
  return Promise.race([tasks.task1(), tasks.task2(), tasks.task3()]);
}
main().then(function(result) {
  console.log(result);
});

// 2

能夠看到,task1 最早完成,而後就拿到他的值進行 then 操做。

原文來自個人博客 http://qiutc.me/post/promise-learn-note.html歡迎你們關注~

相關文章
相關標籤/搜索