Promise 是異步編程的一種解決方案:
從語法上講,promise是一個對象,從它能夠獲取異步操做的消息;從本意上講,它是承諾,承諾它過一段時間會給你一個結果。
promise有三種狀態: pending(等待態),fulfiled(成功態),rejected(失敗態);狀態一旦改變,就不會再變。創造promise實例後,它會當即執行。
在實現以前,能夠先看一下Promise A plus規範jquery
這裏先實現promise最基本的功能:promise建立後當即執行;在then時執行相應的函數;捕獲錯誤當即變成reject態。npm
// promise裏只有一個參數,叫executor(執行器) function Promise(executor) { let self = this; self.status = 'pending';//等待態 self.value = undefined;//默認成功的值 self.err = undefined;//默認失敗的值 function resolve(value) { if (self.status === 'pending') { self.status = 'resolved'; self.value = value; } } function reject(err) { if (self.status === 'pending') { self.status = 'rejected'; self.err = err; } } try {//捕獲時發生異常,直接變成reject態,拋出錯誤 executor(resolve, reject);//promise實例建立後,當即執行 } catch (error) { reject(error); } } //在prototype上定義then實例方法 Promise.prototype.then = function (onFulfilled, onRejected) { let self = this; if (self.status === 'resolved') { onFulfilled(self.value); } if (self.status === 'rejected') { onRejected(self.err); } }
這裏咱們先測試一下咱們的Promise
編程
這裏便實現了基本功能,前面說過Promise 是異步編程的一種解決方案;
咱們加個異步邏輯運行一下:數組
咱們都知道異步代碼並不會當即執行,這時既不是resolved也不是rejected,而是 pending。
在以前的狀態判斷裏面,正好丟了一個pending狀態。OK,這時須要在then裏判斷當status爲pending時,先將onFulfilled, onRejected存入數組裏,當status改變時,再遍歷數組讓裏面的函數依次執行,看代碼。
(1)申明兩個存放onFulfiled,onRejected的數組promise
function Promise(resolver) { let self = this; self.status = 'pending';//等待態 self.value = undefined;//默認成功的值 self.err = undefined;//默認失敗的值 self.onResolvedCallbacks = []; // 存放then成功的回調 self.onRejectedCallbacks = []; // 存放then失敗的回調 function resolve(value) { if (self.status === 'pending') { self.status = 'resolved'; self.value = value; self.onResolvedCallbacks.forEach(fn=>{//調用resolve時,依次執行數組裏的函數 fn(); }) } } function reject(err) { if (self.status === 'pending') { self.status = 'rejected'; self.err = err; self.onRejectedCallbacks.forEach(fn=>{ fn(); }) } } try {//捕獲時發生異常,直接拋出錯誤 resolver(resolve, reject);//promise實例建立後,當即執行它的方法 } catch (error) { reject(error) } }
(2)接着在then方法裏添加pending的判斷異步
Promise.prototype.then = function (onFulfilled, onRejected) { let self = this; if (self.status === 'resolved') { onFulfilled(self.value); } if (self.status === 'rejected') { onRejected(self.err); } if(self.status==='pending'){// 此時沒resolved,也沒rejectd self.onResolvedCallbacks.push(()=>{ onFulfilled(self.value); }); self.onRejectedCallbacks.push(()=>{ onRejected(self.err); }) } }
再看剛剛的異步邏輯
異步編程
1s後就執行成功了,是否是很神奇,再看下面:函數
(1)規範裏說在同一個promise裏then能夠被屢次調用。測試
(2)jquery能實現鏈式調用靠的是返回this,而promise不能返回this,規範裏又說它返回的是一個新的Promise實例(注意,不是原來那個Promise實例);ui
在then裏新建一個promise2併爲每個狀態包一個Promise
寫到這裏,再來看看規範,規範裏說道
(1)x多是一個promise;
(2)多是一個對象或者方法;
(3)也有多是一個普通的值。
這時須要一個方法來處理x
因而引入一個處理方法resolvePromise(promise2, x, resolve, reject);
這裏須要注意一下,有些人寫的promise可能會既調用成功,又調用失敗,若是兩個都調用先調用誰另外一個就忽略掉。
因而增長一個判斷called表示是否調用過成功或者失敗,看代碼:
function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) {//promise2和x不能相同 return reject(new TypeError('循環引用了')) } let called;// 表示是否調用過成功或者失敗 //這裏對x的類型進行判斷 if (x !== null && (typeof x === 'object' || typeof x === 'function')) { try { // 判斷x是否是promise,若是x是對象而且x的then方法是函數咱們就認爲他是一個promise let then = x.then; if (typeof then === 'function') { then.call(x, function (y) { 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 { // 說明是一個普通值1 resolve(x); // 表示成功了 } }
相應的將前面的代碼進行一些更改
若是在then中什麼都不傳,值會穿透到最後調用的時候;
這時須要在then裏給onFulfilled和onRejected寫一個默認的函數
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { return value; } onRejected = typeof onRejected === 'function' ? onRejected : function (err) { throw err;//這裏須要拋出錯誤,不能return err,不然會在下一次調用成功態 }
規範裏要求,全部的onFulfilled和onRejected都要確保異步執行
這裏以resolve爲例,寫一個setTimeout():
在使用promise的過程當中,咱們都須要先new Promise(),好比說:
function read() { let fs = require('fs'); let promise = new Promise(function(resolve,reject){ fs.readFile('./1.txt','utf8',function(err,data){ if(err) reject(err); resolve(data); }) }); return promise }
在Promise中,它爲咱們提供了一個語法糖Promise.defer,用Promise.defer只需這樣寫:
function read() { let defer = Promise.defer() require('fs').readFile('.//1.txt', 'utf8', function (err, data) { if(err) defer.reject(err); defer.resolve(data); }) return defer.promise; }
爲此,再爲咱們的Promise加一個defer方法:
Promise.defer = Promise.deferred = function () { let dfd = {}; dfd.promise = new Promise(function (resolve, reject) { dfd.resolve = resolve; dfd.reject = reject; }); return dfd }
在這裏,咱們基本實現了一個比較完整的promise;固然Promise還有許多靜態方法,還有js的異步發展史,這些能夠在下一次進行討論。
完整代碼:
// promise裏只有一個參數,叫executor(執行器) function Promise(executor) { let self = this; self.status = 'pending';//等待態 self.value = undefined;//默認成功的值 self.err = undefined;//默認失敗的值 self.onResolvedCallbacks = []; // 存放then成功的回調 self.onRejectedCallbacks = []; // 存放then失敗的回調 function resolve(value) { if (self.status === 'pending') { self.status = 'resolved'; self.value = value; self.onResolvedCallbacks.forEach(function (fn) { fn(); }); } } function reject(err) { if (self.status === 'pending') { self.status = 'rejected'; self.err = err; self.onRejectedCallbacks.forEach(function (fn) { fn(); }); } } try {//捕獲時發生異常,直接變成reject態,拋出錯誤 executor(resolve, reject);//promise實例建立後,當即執行 } catch (error) { reject(error); } } function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) {//promise2和x不能相同 return reject(new TypeError('循環引用了')) } let called;// 表示是否調用過成功或者失敗 //這裏對x的類型進行判斷 if (x !== null && (typeof x === 'object' || typeof x === 'function')) { try { // 判斷x是否是promise,若是x是對象而且x的then方法是函數咱們就認爲他是一個promise let then = x.then; if (typeof then === 'function') { then.call(x, function (y) { 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 { // 說明是一個普通值1 resolve(x); // 表示成功了 } } //在prototype上定義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;//這裏須要拋出錯誤,不能return err,不然會在下一次調用成功態 } let self = this; let promise2; //返回的promise if (self.status === 'resolved') { promise2 = new Promise(function (resolve, reject) { setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }) } if (self.status === 'rejected') { promise2 = new Promise(function (resolve, reject) { setTimeout(function () { try { let x = onRejected(self.err); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }) } // 當調用then時可能沒成功 也沒失敗 if (self.status === 'pending') { promise2 = new Promise(function (resolve, reject) { // 此時沒有resolve 也沒有reject self.onResolvedCallbacks.push(function () { setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }) }); self.onRejectedCallbacks.push(function () { setTimeout(function () { try { let x = onRejected(self.err); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }); }) } return promise2; } Promise.defer = Promise.deferred = function () { let dfd = {}; dfd.promise = new Promise(function (resolve, reject) { dfd.resolve = resolve; dfd.reject = reject; }); return dfd } module.exports = Promise;
npm i -g promises-aplus-tests promises-aplus-tests Promise.js