什麼是Promise
Promise 是一個建構函式, new Promise 返回一個 promise物件 接收一個excutor執行函式作為引數, excutor有兩個函式型別形參resolve reject
其用法是let p = new Promise(function (resolve,reject){
resolve("這是promise")
reject("這個不執行")
})
p.then(function (data) {
console.log(data)
},function (err) {
console.log(err)
})複製程式碼
開始寫一個Promise
1,我們從用法中就可以看出new Promise裡面是一個函式,而函式裡的傳參是兩個函式-resolve和reject。要明白,promise執行的時候有三種狀態:
- pending
- resolve(成功)
- reject(失敗)
只要成功就不會失敗,只要失敗就不會成功,不會又成功又失敗。走了resolve就不會走reject,寫了也沒用。promise規範
function Promise(executor) {
let _self = this;
_self.status = "pending";
_self.value = undefined;//成功的原因;
_self.reason = undefined;//失敗的原因;
function resolve(value) {
if(_self.status === "pending"){
_self.status = "resolved" //改變狀態值
_self.value = value //給引數賦值
}
};
function reject(reason) {
_self.status = "rejected"
_self.reason = reason
}
try{ //捕獲異常
executor(resolve,reject); //promise 引數是一個函式,函式中的倆個引數是兩個函式
}catch (e){
reject(e)
}
}
//promise例項上有個then方法。
Promise.prototype.then = function (onFulfilled,onRejected) {
let _sele = this;
if(_sele.status === "resolved"){
onFulfilled(_sele.value)
}
if(_sele.status === "rejected"){
onRejected(_sele.reason)
}
}
module.exports = Promise;複製程式碼
一個簡單的promise模組就寫好了引入執行一下let Promise = require('./promise')
let p = new Promise(function (resolve,reject){
resolve("這是promise")
})
p.then(function (data) {
console.log(data)
},function (err) {
console.log(err)
})複製程式碼
2,如果resolve 是需要非同步執行,如下
let Promise = require('./promise')
let p = new Promise(function (resolve,reject){
setTimeout(function () { //給一個延遲
resolve("這是promise")
})
})
p.then(function (data) {
console.log(data)
},function (err) {
console.log(err)
})複製程式碼
這樣的話執行結果是空,沒有執行出來,因為我們類裡寫的then方法是同步執行,當執行到判斷status的值的時候,因為setTimeout的原因,狀態值還沒改變過來,所以程式碼會一直停在那,所以我們在這種情況下就要給一個空間來存放這些沒有狀態的操作,當狀態明確了再執行。那麼聽起來就需要給一個陣列結構空間了。function Promise(executor) {
let _self = this;
_self.status = "pending";
_self.value = undefined;//成功的原因;
_self.reason = undefined;//失敗的原因;
_self.onResolvedCallbacks = []; // 存放then成功的回撥
_self.onRejectedCallbacks = []; // 存放then失敗的回撥
function resolve(value) { // 成功狀態
if(_self.status === "pending"){
_self.status = "resolved";
_self.value = value;
//判斷好了狀態,要迴圈執行出來,這裡為什麼會是陣列,是因為會有:p.then();p.then(),多次執行的狀態,不過只要有一個成功就是成功
_self.onResolvedCallbacks.forEach(function (fn) {
fn()
})
}
};
function reject(reason) { // 失敗狀態
_self.status = "rejected";
_self.reason = reason;
_self.onRejectedCallbacks.forEach(function (fn) {
fn()
})
}
try{
executor(resolve,reject);
}catch (e){ // 捕獲的時候發生異常,就直接失敗了
reject(e)
}
}
//promise例項上有個then方法。
Promise.prototype.then = function (onFulfilled,onRejected) {
let _sele = this;
if(_sele.status === "resolved"){
onFulfilled(_sele.value)
}
if(_sele.status === "rejected"){
onRejected(_sele.reason)
}
if(_sele.status === "pending"){
//把這個狀態的都儲存起來
_sele.onResolvedCallbacks.push(function () {
onFulfilled(_sele.value)
})
_sele.onRejectedCallbacks.push(function () {
onRejected(_sele.reason)
})
}
}
module.exports = Promise;複製程式碼
3,鏈式呼叫,promise可以實現鏈式呼叫。方便又實惠。執行程式碼如下:let promise = new Promise(function (resolve,reject) {
resolve(100)
})
//鏈式呼叫的特點,將第一次成功的返回值當成下一次回撥函式的引數
promise.then(function (data) {
throw new Error("123")
},function () {
}).then(function (data) {
console.log(data);
},function(err){
console.log(err)
})
//鏈式呼叫:靠的是返回一個新的promise複製程式碼
但其實也可以這樣寫:let p2 = promise.then(function (data) {
return data //這裡返回的只要不是錯誤,下次走的還是成功,
},function () {
})
p2.then(function (data) {
console.log(data);
},function(err){
console.log(err)
})複製程式碼
這裡不等價於(返回資料的化直接讓下一次的promise 成功 如果返回的是promise就等待這個promise執行結果)
let p2 = promise.then(function (data) {
return new Promise(function (resolve,reject) {
resolve(data)
})
},function () {
})
p2.then(function (data) {
console.log(data);
},function(err){
console.log(err)
})複製程式碼
正確的執行結果應該是100,那麼開始寫吧,(重頭戲)function Promise(executor) {
let _self = this;
_self.status = "pending";
_self.value = undefined;//成功的原因;
_self.reason = undefined;//失敗的原因;
_self.onResolvedCallbacks = []; // 存放then成功的回撥
_self.onRejectedCallbacks = []; // 存放then失敗的回撥
function resolve(value) { // 成功狀態
if(_self.status === "pending"){
_self.status = "resolved";
_self.value = value;
//判斷好了狀態,要迴圈執行出來,這裡為什麼會是陣列,是因為會有:p.then();p.then(),多次執行的狀態,不過只要有一個成功就是成功
_self.onResolvedCallbacks.forEach(function (fn) {
fn()
})
}
};
function reject(reason) { // 失敗狀態
_self.status = "rejected";
_self.reason = reason;
_self.onRejectedCallbacks.forEach(function (fn) {
fn()
})
}
try{
executor(resolve,reject);
}catch (e){ // 捕獲的時候發生異常,就直接失敗了
reject(e)
}
}
//處理返回結果
function resolvePromise(promise2,x,resolve,reject) {
//
if(promise2 === x){ //首先要判斷返回的x不能跟原來的promise相等,如果是的話就是自己等待自己了!。
return reject(new TypeError("不能迴圈引用")); //這裡應該報一個型別錯誤,有問題
}
let called; //表示是否調過
if(x !== null &&(typeof x ==='object'||typeof x ==='function')){ //判斷返回的x是不是一個promise,promise應該是一個物件;這裡要把null排除因為typeof null是object
try{
// 1,看看返回的這個物件或方法裡有沒有then方法
let then = x.then;//這裡then可能是{then:1}
if(typeof then === 'function'){ //說明是一個promise
then.call(x,function (y) { //call是為了this指向x,
//2,再看是否被呼叫過
if(called) return ; //表示是否呼叫過成功或者失敗
called = true;
// y可能還是一個promise,在去解析直到返回的是一個普通值
resolvePromise(promise2,y,resolve,reject)
},function (err) { //表示失敗了
if(called) return ;
called = true;
reject(err)
})
}else{
resolve(x)
}
} catch (e){
if(called) return ;
called = true;
reject(e)
}
}else{
resolve(x) //如果是常量就直接resolve;
}
}
//promise例項上有個then方法。
Promise.prototype.then = function (onFulfilled,onRejected) {
let _sele = this;
//首先要先定義一個新的promise
let promise2;
if(_sele.status === "resolved"){
promise2 = new Promise(function (resolve,reject) {
setTimeout(function () { //為了測試時 效果統一 因為很多人的實現 onfufiled 和 onrejected是非同步執行,為了保證測試統一 所以全部非同步
try { //捕獲異常
let x = onFulfilled(_sele.value) //宣告一個x來接受返回值
resolvePromise(promise2,x,resolve,reject)
}catch (e){
reject(e)
}
})
})
}
if(_sele.status === "rejected"){
promise2 = new Promise(function (resolve,reject) {
setTimeout(function () { //為了測試時 效果統一 因為很多人的實現 onfufiled 和 onrejected是非同步執行,為了保證測試統一 所以全部非同步
try { //捕獲異常
let x = onRejected(_sele.reason) //宣告一個x來接受返回值
resolvePromise(promise2,x,resolve,reject)
}catch (e){
reject(e)
}
})
})
}
// 當呼叫then時可能沒成功 也沒失敗
if(_sele.status === "pending"){
promise2 = new Promise(function (resolve,reject) {
//把這個狀態的都儲存起來
_sele.onResolvedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onFulfilled(_sele.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
_sele.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onRejected(_sele.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
})
}
return promise2;
}
module.exports = Promise;複製程式碼
對於promise2 === x 什麼情況下出現
var p2 = promise.then(function (data) {
return p2
},function () {
})
p2.then(function (data) {
console.log(data);
},function(err){
console.log(err)
})複製程式碼
4,如果有人這樣執行程式碼也要考慮進去
let p2 = promise.then(function (data) {
return new Promise(function (resolve,reject) {
resolve(data)
})
},function () {
})
p2.then().then().then(function (data) {//多個then()空呼叫
console.log(data);
},function(err){
console.log(err)
})複製程式碼
那我們就要對類裡then方法裡的回撥做判斷了 if(_self.status === "pending"){function Promise(executor) {
let _self = this;
_self.status = "pending";
_self.value = undefined;//成功的原因;
_self.reason = undefined;//失敗的原因;
_self.onResolvedCallbacks = []; // 存放then成功的回撥
_self.onRejectedCallbacks = []; // 存放then失敗的回撥
function resolve(value) { // 成功狀態
// value可能是別人的promise
if(value !==null&&(typeof value === 'object'||typeof value === 'function')){ //這種情況是為了判斷 resolve(New promise)
return value.then(resolve,reject)
}
if(_self.status === "pending"){
_self.status = "resolved";
_self.value = value;
//判斷好了狀態,要迴圈執行出來,這裡為什麼會是陣列,是因為會有:p.then();p.then(),多次執行的狀態,不過只要有一個成功就是成功
_self.onResolvedCallbacks.forEach(function (fn) {
fn()
})
}
};
function reject(reason) { // 失敗狀態
_self.status = "rejected";
_self.reason = reason;
_self.onRejectedCallbacks.forEach(function (fn) {
fn()
})
}
try{
executor(resolve,reject);
}catch (e){ // 捕獲的時候發生異常,就直接失敗了
reject(e)
}
}
//處理返回結果
function resolvePromise(promise2,x,resolve,reject) {
if(promise2 === x){ //首先要判斷返回的x不能跟原來的promise相等。
return reject(new TypeError("不能迴圈引用")); //這裡應該報一個型別錯誤,有問題
}
let called; //表示是否調過
if(x !== null &&(typeof x ==='object'||typeof x ==='function')){ //判斷返回的x是不是一個promise,promise應該是一個物件;這裡要把null排除因為typeof null是object
try{
// 1,看看返回的這個物件或方法裡有沒有then方法
let then = x.then;//這裡then可能是{then:1}
if(typeof then === 'function'){ //說明是一個promise
then.call(x,function (y) { //call是為了this指向x,
//2,再看是否被呼叫過
if(called) return ; //表示是否呼叫過成功或者失敗
called = true;
// y可能還是一個promise,在去解析直到返回的是一個普通值
resolvePromise(promise2,y,resolve,reject)
},function (err) { //表示失敗了
if(called) return ;
called = true;
reject(err)
})
}else{
resolve(x)
}
} catch (e){
if(called) return ;
called = true;
reject(e)
}
}else{
resolve(x) //如果是常量就直接resolve;
}
}
//promise例項上有個then方法。
Promise.prototype.then = function (onFulfilled,onRejected) {
//成功和失敗預設不穿給一個函式
onFulfilled = typeof onFulfilled ==='function'?onFulfilled:function (value) {
return value
};
onRejected = typeof onRejected ==='function' ? onRejected :function (err) {
throw err
}
let _sele = this;
//首先要先定義一個新的promise
let promise2;
if(_sele.status === "resolved"){
promise2 = new Promise(function (resolve,reject) {
setTimeout(function () { //為了測試時 效果統一 因為很多人的實現 onfufiled 和 onrejected是非同步執行,為了保證測試統一 所以全部非同步
try { //捕獲異常
let x = onFulfilled(_sele.value) //宣告一個x來接受返回值
resolvePromise(promise2,x,resolve,reject)
}catch (e){
reject(e)
}
})
})
}
if(_sele.status === "rejected"){
promise2 = new Promise(function (resolve,reject) {
setTimeout(function () { //為了測試時 效果統一 因為很多人的實現 onfufiled 和 onrejected是非同步執行,為了保證測試統一 所以全部非同步
try { //捕獲異常
let x = onRejected(_sele.reason) //宣告一個x來接受返回值
resolvePromise(promise2,x,resolve,reject)
}catch (e){
reject(e)
}
})
})
}
// 當呼叫then時可能沒成功 也沒失敗
if(_sele.status === "pending"){
promise2 = new Promise(function (resolve,reject) {
//把這個狀態的都儲存起來
_sele.onResolvedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onFulfilled(_sele.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
_sele.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onRejected(_sele.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
})
}
return promise2;
}
module.exports = Promise;複製程式碼
好了,一個完整的promise寫完了,同學們聯絡的時候一定要記得引入自己寫的promise檔案哦!對於以上程式碼實現,我們可以通過一個promises-aplus-tests的庫來驗證是否達成規範要求
可以通過npm install -g promises-aplus-tests,使用時直接promises-aplus-tests 檔名即可。
在Promise類上的掛方法
實現promise類上的 call,catch,race,resolve,reject等方法
// 捕獲錯誤的方法
Promise.prototype.catch = function (callback) {
return this.then(null, callback)
}
// 解析全部方法
// let arr = [];
// arr[1] = 100;
// console.log(arr.length)
Promise.all = function (promises) {
//promises是一個promise的陣列
return new Promise(function (resolve, reject) {
let arr = []; //arr是最終返回值的結果
let i = 0; // 表示成功了多少次
function processData(index, y) {
arr[index] = y;
if (++i === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (y) {
processData(i, y)
}, reject)
}
})
}
// 只要有一個promise成功了 就算成功。如果第一個失敗了就失敗了
Promise.race = function (promises) {
return new Promise(function (resolve, reject) {
for (var i = 0; i < promises.length; i++) {
promises[i].then(resolve,reject)
//這裡詳細解釋一下
//promise[i].then(function(data){
// resolve(data) //只接收一個resolve,因為他們同屬於最外層的new promise。
// },reject)
}
})
}
// 生成一個成功的promise
Promise.resolve = function(value){
return new Promise(function(resolve,reject){
resolve(value);
})
}
// 生成一個失敗的promise
Promise.reject = function(reason){
return new Promise(function(resolve,reject){
reject(reason);
})
}
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd
}複製程式碼
用法如下Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd
}
let Promise = require('./Promise');
function read() {
let defer = Promise.defer()
require('fs').readFile('./2.promise/1.txt', 'utf8', function (err, data) {
if(err) defer.reject(err);
defer.resolve(data);
})
return defer.promise;
}
read().then(function (data) {
console.log(data)
},function(err){
console.log(err);
})
複製程式碼