成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

實現(xiàn)一個自己的Promise

leone / 3318人閱讀

摘要:前言在寫這個之前,希望你已經(jīng)對中的很熟悉了,概念性和基礎(chǔ)的東西就不再講了,不懂的同學(xué)可以去看看阮一峰老師的教程我主要按以下個步驟來一步一步實現(xiàn),異步的實現(xiàn)我放在了后面,所以前面幾步暫不考慮實現(xiàn)一個基本的實現(xiàn)的鏈?zhǔn)秸{(diào)用處理函數(shù)的參數(shù)是實例的情

前言

在寫這個promise之前,希望你已經(jīng)對es6中的Promise很熟悉了,概念性和基礎(chǔ)的東西就不再講了,不懂的同學(xué)可以去看看阮一峰老師的es6教程. 我主要按以下5個步驟來一步一步實現(xiàn),異步的實現(xiàn)我放在了后面,所以前面幾步暫不考慮

實現(xiàn)一個基本的MyPromise

實現(xiàn)then的鏈?zhǔn)秸{(diào)用

處理reolve函數(shù)的參數(shù)是MyPromise實例的情況以及處理then方法中前一個回調(diào)函數(shù)返回的也是一個MyPromise實例的情況

實現(xiàn)異步的MyPromise

完善MyPromise的其它方法

1.實現(xiàn)一個基本的MyPromise
/*
 * 這里我將promise的3個狀態(tài)分別定義為: pending, resolved, rejected
 * 其中fn必須是個函數(shù), 必須通過new來使用
 */
function MyPromise(fn) {
  if (!(this instanceof MyPromise)) {
    throw new TypeError("MyPromise must be constructed via new");
  }
  if (typeof fn !== "function") {
    throw new TypeError("MyPromise constructor argument is not a function");
  }
  this.state = "pending";  // 出初始化狀態(tài)
  this.value = undefined;  // 初始化一個值, 用來存儲resolve或者reject的值
  // 執(zhí)行 fn 方法
  executeFn(fn, this);
}

MyPromise.prototype.then = function(onFullfilled, onRejected) {
  var res = undefined;
  var cb = this.state === "resolved" ? onFullfilled : onRejected;
  res = cb(this.value);
}

// 執(zhí)行 fn 方法
function executeFn(fn, promise) {
  var done = false;     // 聲明一個變量, 防止resolve, reject連續(xù)調(diào)用
  try {
    fn(function _resolve(value) {
      if(done) return;
      done = true;
      resolve(promise, value);
    }, function _reject(reason) {
      if(done) return;
      done = true;
      reject(promise, reason);
    });
  }
  catch(err) {
    if(!done) {
      done = true;
      reject(promise, err);
    }
  }
}

function resolve(promise, value) {
  promise.state = "resolved";
  promise.value = value;
}

function reject(promise, error) {
  promise.state = "rejected";
  promise.value = error;
}

這樣就實現(xiàn)了一個基本版的MyPromise,調(diào)用方法同Promise,如下:

new MyPromise((resolve, reject) => {
  // resolve("resolved");
  reject("rejected");
}).then(res => {
  console.log(">>> res", res);
}, err =>  {
  console.log(">>> err", err);
});
2.實現(xiàn)then的鏈?zhǔn)秸{(diào)用

原生Promise支持鏈?zhǔn)秸{(diào)用,并且then方法會返回一個新的Promise實例,第一個回調(diào)函數(shù)完成以后,會將返回結(jié)果作為參數(shù),傳入第二個回調(diào)函數(shù),所以我們可以修改下then方法,其余不變

MyPromise.prototype.then = function(onFullfilled, onRejected) {
  var self = this;
  var res = undefined;
  var cb = this.state === "resolved" ? onFullfilled : onRejected;
  var newPromise = new MyPromise(function(_resolve, _reject) {
    try {
      res = cb(self.value);
      _resolve(res);
    }
    catch(err) {
      _reject(err);
    }
  });

  return newPromise;
}

這樣的話,鏈?zhǔn)秸{(diào)用也就實現(xiàn)了,測試了下,沒啥問題

new MyPromise((resolve, reject) => {
  resolve("resolved");
  // reject("rejected");
}).then(res => {
  console.log(">>> res", res);
  return "res1";
}, err => {
  console.log(">>> err", err);
  return "err1";
}).then(res2 => {
  console.log(">>> res2", res2);
 }, err2 => {
  console.log(">>> err2", err2);
});
3. 處理reolve函數(shù)的參數(shù)是MyPromise實例的情況以及處理then方法中前一個回調(diào)函數(shù)返回的也是一個MyPromise實例的情況

resolve函數(shù)的參數(shù)除了正常的值以外,還可能是另一個MyPromise實例,如下,這個時候p1的狀態(tài)決定了p2的狀態(tài)

const p1 = new MyPromise(function(resolve, reject) {
  // ...
});

const p2 = new MyPromise(function(resolve, reject) {
  // ...
  resolve(p1);
});

同樣,在then方法中,前一個回調(diào)函數(shù)有可能返回的也是一個MyPromise對象,這時后一個回調(diào)函數(shù)就會等待該MyPromise對象的狀態(tài)發(fā)生變化,才會被調(diào)用,例如:

const p1 = new MyPromise(function(resolve, reject) {
  // ...
});

const p2 = new MyPromise(function(resolve, reject) {
  // ...
  resolve("p2 resolved");
});
p2.then(res1 => {
  console.log(">>> res1", res1);
  return p1;
}, err1 => {
  console.log(">>> err1", err1);
}).then(res2 => {
  console.log(">>> res2", res2);
}, err2 => {
  console.log(">>> err2", err2);
})

了解以上后,我們來實現(xiàn)它

function resolve(promise, value) {
  if(!handlePromise(promise, value)) return;  // 增加這行

  promise.state = "resolved";
  promise.value = value;
}
/*
 * 增加一個函數(shù)用來處理返回值或者resolve的參數(shù)是promise的情況, 最后的返回值起個標(biāo)識作用
 */
function handlePromise(promise, value) {
  if(value === promise) {
    reject(promise, "A promise cannot be resolved with itself");
    return;
  }
  if(value && (typeof value === "object" || typeof value === "function")) {
    var then = value.then;
    if(typeof then === "function") {
      executeFn(then.bind(value), promise);
      return;
    }
  }
  return true;
}
// 同時then中增加一行代碼
MyPromise.prototype.then = function(onFullfilled, onRejected) {
  var self = this;
  var res = undefined;
  var cb = this.state === "resolved" ? onFullfilled : onRejected;
  var newPromise = new MyPromise(function(_resolve, _reject) {
    if(self.state === "pending") return;  // 增加這行
    try {
      res = cb(self.value);
      _resolve(res);
    }
    catch(err) {
      _reject(err);
    }
  });

  return newPromise;
}

到這里,就解決了以上2個問題,測試一下

const p = new MyPromise((resolve, reject) => {
  // resolve("resolve a promise");
  reject("reject a promise");
});
// resolve參數(shù)是MyPromise實例
new MyPromise((resolve, reject) => {
  resolve(p);
  // reject("rejected");
}).then(res => {
  console.log(">>> res", res);
  return "res1";
}, err => {
  console.log(">>> err", err);
  return "err1";
}).then(res2 => {
  console.log(">>> res2", res2);
}, err2 => {
  console.log(">>> err2", err2);
});

// then第一個方法返回的是MyPromise實例
new MyPromise((resolve, reject) => {
  resolve("resolved");
  // reject("rejected");
}).then(res => {
  console.log(">>> res", res);
  return p;
}, err => {
  console.log(">>> err", err);
  return "err1";
}).then(res2 => {
  console.log(">>> res2", res2);
}, err2 => {
  console.log(">>> err2", err2);
});
4. 實現(xiàn)異步的MyPromise

經(jīng)過前面3步,MyPromise的已經(jīng)實現(xiàn)了大半,接著我們來實現(xiàn)異步的MyPromise. 既然是異步,我們并不知道它什么時候結(jié)束,但是我們可以將它的異步回調(diào)存入一個數(shù)組,待它結(jié)束后執(zhí)行它,好吧,其實就是觀察者模式了

首先在構(gòu)造函數(shù)中加上一句

function MyPromise(fn) {
  // ...這里省略,加上下面這行
  this.callbacks = [];     // 存儲異步的回調(diào)方法
  // 執(zhí)行 fn 方法
  executeFn(fn, this);
}

然后在resolve和reject方法中分別加上

function resolve(promise, value) {
  // ...這里省略,加上下面幾句
  promise.callbacks.forEach(function(cb) {
    cb();
  });
}
function reject(promise, error) {
  // ...這里省略,加上下面幾句
  promise.callbacks.forEach(function(cb) {
    cb();
  });
}

最后在then方法中,將異步的回調(diào)發(fā)存入數(shù)組中

MyPromise.prototype.then = function(onFullfilled, onRejected) {
  // ...這里省略,不變
  var newPromise = new MyPromise(function(_resolve, _reject) {
    // 狀態(tài)為pending時,將回調(diào)存入數(shù)組,因為then中方法也是異步執(zhí)行
    // 所以用setTimeout,同時直接return
    if(self.state === "pending") {
      self.callbacks.push(function() {
        setTimeout(function() {
          // 這里需要再次判斷
          cb = self.state === "resolved" ? onFullfilled : onRejected;
          try {
            res = cb(self.value);
            _resolve(res);
          }
          catch(err) {
            _reject(err);
          }
        });
      });
      return;
    }
    // then中是異步執(zhí)行
    setTimeout(function() {
      try {
        res = cb(self.value);
        _resolve(res);
      }
      catch(err) {
        _reject(err);
      }
    });
  });

  return newPromise;
}

到這里,異步的MyPromise也就實現(xiàn)了,then方法代碼有點亂,我們整理下

MyPromise.prototype.then = function(onFullfilled, onRejected) {
  // 這里刪除了一部分代碼
  var self = this;
  var newPromise = new MyPromise(function(_resolve, _reject) {
    if(self.state === "pending") {
      self.callbacks.push(function() {
        // 同時將這部分的代碼抽成了以下方法
        handleResolved(self, onFullfilled, onRejected, _resolve, _reject);
      });
      return;
    }
    handleResolved(self, onFullfilled, onRejected, _resolve, _reject);
  });

  return newPromise;
}
function handleResolved(promise, onFullfilled, onRejected, _resolve, _reject) {
  setTimeout(function() {
    var res = undefined;
    var cb = promise.state === "resolved" ? onFullfilled : onRejected;
    // 需要對cb進行判斷
    if(typeof cb !== "function") {
      if(promise.state === "resolved") {
        _resolve(promise.value);
      }
      else {
        _reject(promise.value);
      }
      return;
    }
    try {
      res = cb(promise.value);
      _resolve(res);
    }
    catch(err) {
      _reject(err);
    }
  });
}

測試如下, 當(dāng)然沒啥問題了

const p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("resolve a promise");
    // reject("reject a promise");
  }, 2 * 1000);
});

new MyPromise((resolve, reject) => {
  resolve(p);
  // reject("rejected");
}).then(res => {
  console.log(">>> res", res);
  return "res1";
}, err => {
  console.log(">>> err", err);
  return "err1";
}).then(res2 => {
  console.log(">>> res2", res2);
}, err2 => {
  console.log(">>> err2", err2);
});

到這里,一個大概的MyPromise也就基本實現(xiàn)完成了,整理后的完整代碼如下

function MyPromise(fn) {
  if (!(this instanceof MyPromise)) {
    throw new TypeError("MyPromise must be constructed via new");
  }
  if (typeof fn !== "function") {
    throw new TypeError("MyPromise constructor argument is not a function");
  }
  this.state = "pending";  // 初始化狀態(tài)
  this.value = undefined;  // 初始化一個值, 用來存儲resolve或者reject的值
  this.callbacks = [];     // 存儲異步的回調(diào)方法
  // 執(zhí)行 fn 方法
  executeFn(fn, this);
}

MyPromise.prototype.then = function(onFullfilled, onRejected) {
  var self = this;
  var newPromise = new MyPromise(function(_resolve, _reject) {
    if(self.state === "pending") {
      self.callbacks.push(function() {
        handleResolved(self, onFullfilled, onRejected, _resolve, _reject);
      });
      return;
    }
    handleResolved(self, onFullfilled, onRejected, _resolve, _reject);
  });

  return newPromise;
}

function handleResolved(promise, onFullfilled, onRejected, _resolve, _reject) {
  setTimeout(function() {
    var res = undefined;
    var cb = promise.state === "resolved" ? onFullfilled : onRejected;
    if(typeof cb !== "function") {
      if(promise.state === "resolved") {
        _resolve(promise.value);
      }
      else {
        _reject(promise.value);
      }
      return;
    }
    try {
      res = cb(promise.value);
      _resolve(res);
    }
    catch(err) {
      _reject(err);
    }
  });
}

// 執(zhí)行 fn 方法
function executeFn(fn, promise) {
  var done = false;     // 聲明一個變量, 防止resolve, reject連續(xù)調(diào)用
  try {
    fn(function _resolve(value) {
      if(done) return;
      done = true;
      resolve(promise, value);
    }, function _reject(reason) {
      if(done) return;
      done = true;
      reject(promise, reason);
    });
  }
  catch(err) {
    if(!done) {
      done = true;
      reject(promise, err);
    }
  }
}

function resolve(promise, value) {
  if(!handlePromise(promise, value)) return;

  promise.state = "resolved";
  promise.value = value;
  promise.callbacks.forEach(function(cb) {
    cb();
  });
}

function reject(promise, error) {
  promise.state = "rejected";
  promise.value = error;
  promise.callbacks.forEach(function(cb) {
    cb();
  });
}

// 用來處理返回值或者resolve的參數(shù)是promise的情況, 最后的返回值起個標(biāo)識作用
function handlePromise(promise, value) {
  if(value === promise) {
    reject(promise, "A promise cannot be resolved with itself");
    return;
  }
  if(value && (typeof value === "object" || typeof value === "function")) {
    var then = value.then;
    if(typeof then === "function") {
      executeFn(then.bind(value), promise);
      return;
    }
  }
  return true;
}
5. 最后來實現(xiàn)下MyPromise的其它方法
MyPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
}

// 只要不是pending狀態(tài)都會執(zhí)行
MyPromise.prototype.finally = function(cb) {
  return this.then(
    function(value) {
      return MyPromise.resolve(cb()).then(function() {
        return value;
      });
    },
    function(err) {
      return MyPromise.resolve(cb()).then(function() {
        throw err;
      });
    }
  );
}

MyPromise.resolve = function(val) {
  return new MyPromise(function(resolve, reject) {
    resolve(val);
  });
}

MyPromise.reject = function(err) {
  return new MyPromise(function(resolve, reject) {
    reject(err);
  });
}

/*
 * all方法用于將多個 MyPromise 實例,包裝成一個新的 MyPromise 實例
 * 只有全部實例都resolved,才會resolve; 只要其中一個rejected,就會reject
 * 參數(shù)可以不是數(shù)組,但必須具有 Iterator 接口, 同時里面的值可能也不是promise實例
 */
MyPromise.all = function(promiseArr) {
  var args = [].slice.call(promiseArr);

  return new MyPromise(function(resolve, reject) {
    var arr = [];
    var resolveCount = 0;
    var argsLen = args.length;
    for(var i = 0; i < argsLen; i++) {
      handle(i, args[i]);
    }
    function handle(index, val) {
      MyPromise.resolve(val).then(function(value) {
        arr[index] = value;
        if(++resolveCount === argsLen) {
          resolve(arr);
        }
      }, reject);
    }  
  });
}

/*
 * race方法與all方法類似,只要其中一個實例狀態(tài)發(fā)生改變resolved / rejected即可
 * 參數(shù)可以不是數(shù)組,但必須具有 Iterator 接口, 同時里面的值可能也不是promise實例
 */
MyPromise.race = function(promiseArr) {
  var args = [].slice.call(promiseArr);
  return new MyPromise(function(resolve, reject) {
    for(var i = 0; i < args.length; i++) {
      MyPromise.resolve(args[i]).then(resolve, reject);
    }
  });
}

至此Promise的實現(xiàn)就算完成了,完整代碼的地址點這里

最后

說點題外話,在面試的過程中,經(jīng)常會遇見面試官要求現(xiàn)場實現(xiàn)一個Promise,看了這篇文章后希望對你有所幫助,已經(jīng)能夠?qū)崿F(xiàn)一個Promise了,而對于面試中其他的promise的相關(guān)問題能夠輕松應(yīng)對

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99981.html

相關(guān)文章

  • 我能不能在不看別人怎么實現(xiàn)promise情況下,自己實現(xiàn)一個promise?

    摘要:上網(wǎng)查了一下好像是標(biāo)準(zhǔn)第二步開寫構(gòu)造函數(shù)什么都不想,先寫一個構(gòu)造函數(shù),就叫把。對構(gòu)造函數(shù)再次做修改。并且可以一個值。給下一個繼續(xù)調(diào)用。應(yīng)該是一個新的。最后的版本總結(jié)后來去看了看別人實現(xiàn)的方法。 我能不能在不看別人怎么實現(xiàn)promise的情況下,自己實現(xiàn)一個promise? 都8102年為什么還要寫promise實現(xiàn)? ? 年前和年后面試了幾家公司, 雖然都掛了… 但是都談到了一個...

    JouyPub 評論0 收藏0
  • 從0開始構(gòu)建自己前端知識體系-JS-跟著規(guī)范學(xué)Promise

    摘要:本文僅限瀏覽器環(huán)境測試,環(huán)境可能會不一致狀態(tài)一個實例只能處于三種狀態(tài)中的一種。每次創(chuàng)建的實例都會處于狀態(tài),并且只能由變?yōu)榛驙顟B(tài)。可以認為在實現(xiàn)里與中的都為解決程序。 前言 Promise作為ES6極為重要的一個特性,將我們從無限的回調(diào)地獄中解脫出來,變?yōu)殒準(zhǔn)降木帉懟卣{(diào),大大提高的代碼的可讀性。 使用Promise是極為簡單的,但只停留在會使用階段還是會讓我們不知不覺踩到一些坑的。本文會...

    kelvinlee 評論0 收藏0
  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個接口,可通過...

    tuniutech 評論0 收藏0
  • 自己動手實現(xiàn)一個Promise

    摘要:意味著操作成功完成。狀態(tài)的對象可能觸發(fā)狀態(tài)并傳遞一個值給相應(yīng)的狀態(tài)處理方法,也可能觸發(fā)失敗狀態(tài)并傳遞失敗信息。測試用例測試用例方法返回一個帶有拒絕原因參數(shù)的對象。 Promise基本用法 Promise 對象是一個代理對象,被代理的值在Promise對象創(chuàng)建時可能是未知的。 它允許你為異步操作的成功和失敗分別綁定相應(yīng)的處理方法(handlers)。 這讓異步方法可以像同步方法那樣返回值...

    Yujiaao 評論0 收藏0
  • 徹底理解Promise對象——用es5語法實現(xiàn)一個自己Promise(上篇)

    摘要:鏈?zhǔn)秸{(diào)用在的使用中,我們一定注意到,是可以鏈?zhǔn)秸{(diào)用的很顯然,要實現(xiàn)鏈?zhǔn)秸{(diào)用,方法的返回值也必須是一個對象,這樣才能再次在后面調(diào)用。一種情況下,前一個的或者的返回值是普通的對象,這種情況下我們目前的可以正確處理。 本文同步自我的個人博客: http://mly-zju.github.io/ 眾所周知javascript語言的一大特色就是異步,這既是它的優(yōu)點,同時在某些情況下也帶來了一些的...

    YJNldm 評論0 收藏0
  • 從零開始實現(xiàn)一個自己Promise

    摘要:所以,這篇文章我會帶大家從零開始,手寫一個基本能用的。首先,規(guī)定對象是一個構(gòu)造函數(shù),用來生成實例。然后,這個構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是和。對象通過自身的狀態(tài),來控制異步操作。 剛開始寫前端的時候,處理異步請求經(jīng)常用callback,簡單又順手。后來寫著寫著就拋棄了callback,開始用promise來處理異步問題。promise寫起來確實更加優(yōu)美,但由于缺乏...

    paulquei 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<