Promise
手寫一個PromiseA+的完成。注重這裏只是模仿,實際上原生的promise在事件行列中屬於microTask。這裏用setTimeout模仿不是迥殊適當。由於setTimeout是一個macroTask。
1. 最簡樸的基本功能
/**
* 定義Promise
* 先完成一個最簡樸的。用setTimeout模仿一個異步的要求。
*/
function Promise(fn){
var value= null;
var callbacks = [];
this.then = function(onFulfilled) {
callbacks.push(onFulfilled);
}
function resolve(value){
callbacks.forEach(function(cb){
cb(value);
})
}
fn(resolve);
}
// 運用Promise
var p = new Promise(function(resolve){
setTimeout(function(){
resolve('這是相應的數據')
},2000)
})
p.then(function(response){
console.log(response);
})
2.鏈式挪用
/**
* 先看一下前一個例子存在的題目
* 1.在前一個例子中不停挪用then須要支撐鏈式挪用,每次實行then都要返回挪用對象自身。
* 2.在前一個例子中,當鏈式挪用的時刻,每次then中的值都是同一個值,這是有題目的。實在第一次then中的返回值,應當是第二次挪用then中的函數的參數,順次類推。
* 所以,我們進一步優化一下代碼。
*
*/
function Promise(fn){
var value= null;
var callbacks = [];
this.then = function(onFulfilled) {
callbacks.push({f:onFulfilled});
return this;
}
function resolve(value){
callbacks.map(function(cb,index){
if(index === 0){
callbacks[index].value = value;
}
var rsp = cb.f(cb.value);
if(typeof callbacks[index+1] !== 'undefined'){
callbacks[index+1].value = rsp;
}
})
}
fn(resolve);
}
// 運用Promise
var p = new Promise(function(resolve){
setTimeout(function(){
resolve('這是相應的數據')
},2000)
})
p.then(function(response){
console.log(response);
return 1;
}).then(function(response){
console.log(response);
return 2;
}).then(function(response){
console.log(response);
})
3. 異步
/**
* 先看一下前一個例子存在的題目
* 1. 假如在then要領註冊回調之前,resolve函數就實行了,怎麼辦?比方 new Promise的時刻傳入的函數是同步函數的話,
* then還沒被註冊,resolve就實行了。。這在PromiseA+範例中是不允許的,範例明確要求回調須要經由過程異步的體式格局實行。
* 用來保證一致牢靠的實行遞次。
*
* 因而我們須要到場一些處置懲罰。把resolve里的代碼放到異步行列中去。這裏我們應用setTimeout來完成。
* 道理就是經由過程setTimeout機制,將resolve中實行回調的邏輯安排到JS使命行列末端,以保證在resolve實行時,
* then要領的回調函數已註冊完成
*
*/
function Promise(fn){
var value= null;
var callbacks = [];
this.then = function(onFulfilled) {
callbacks.push({f:onFulfilled});
return this;
}
function resolve(value){
setTimeout(function(){
callbacks.map(function(cb,index){
if(index === 0){
callbacks[index].value = value;
}
var rsp = cb.f(cb.value);
if(typeof callbacks[index+1] !== 'undefined'){
callbacks[index+1].value = rsp;
}
})
},0)
}
fn(resolve);
}
// 運用Promise,如今即使是同步的立馬resolve,也能一般運轉了。
var p = new Promise(function(resolve){
resolve('這是相應的數據')
})
p.then(function(response){
console.log(response);
return 1;
}).then(function(response){
console.log(response);
return 2;
}).then(function(response){
console.log(response);
})
4. 狀況機制
/**
* 先看一下前一個例子存在的題目
* 1.前一個例子還存在一些題目,假如Promise異步操縱已勝利,在這之前註冊的一切回調都邑實行,
* 然則在這以後再註冊的回調函數就不再實行了。詳細的運轉下面這段代碼,能夠看到“can i invoke”並沒有打印出來
* 想要處理這個題目,我們就須要到場狀況機制了。詳細完成看本文件夾下的另一個js文件里的代碼。
*
*/
function Promise(fn){
var value= null;
var callbacks = [];
this.then = function(onFulfilled) {
callbacks.push({f:onFulfilled});
return this;
}
function resolve(value){
setTimeout(function(){
callbacks.map(function(cb,index){
if(index === 0){
callbacks[index].value = value;
}
var rsp = cb.f(cb.value);
if(typeof callbacks[index+1] !== 'undefined'){
callbacks[index+1].value = rsp;
}
})
},0)
}
fn(resolve);
}
//
var p = new Promise(function(resolve){
resolve('這是相應的數據')
})
p.then(function(response){
console.log(response);
return 1;
}).then(function(response){
console.log(response);
return 2;
}).then(function(response){
console.log(response);
})
setTimeout(function(){
p.then(function(response){
console.log('can i invoke?');
})
},0)
/**
* 在promise01.js中,我們已剖析了,我們須要到場狀況機制
* 在這裏完成一下PromiseA+中關於狀況的範例。
*
* Promises/A+範例中的2.1Promise States中明確規定了,pending能夠轉化為fulfilled或rejected而且只能轉化一次,
* 也就是說假如pending轉化到fulfilled狀況,那末就不能再轉化到rejected。
* 而且fulfilled和rejected狀況只能由pending轉化而來,兩者之間不能相互轉換
*
*/
function Promise(fn){
var status = 'pending'
var value= null;
var callbacks = [];
this.then = function(onFulfilled) {
// 假如是pending狀況,則到場到註冊行列中去。
if(status === 'pending'){
callbacks.push({f:onFulfilled});
return this;
}
// 假如是fulfilled 狀況,此時直接實行傳入的註冊函數即可。
onFulfilled(value);
return this;
}
function resolve(newValue){
value = newValue;
status = 'fulfilled';
setTimeout(function(){
callbacks.map(function(cb,index){
if(index === 0){
callbacks[index].value = newValue;
}
var rsp = cb.f(cb.value);
if(typeof callbacks[index+1] !== 'undefined'){
callbacks[index+1].value = rsp;
}
})
},0)
}
fn(resolve);
}
//
var p = new Promise(function(resolve){
resolve('這是相應的數據')
})
p.then(function(response){
console.log(response);
return 1;
}).then(function(response){
console.log(response);
return 2;
}).then(function(response){
console.log(response);
})
setTimeout(function(){
p.then(function(response){
console.log('can i invoke?');
})
},1000)
/**
* 適才的例子中,確切打印出了 can i invoke,然則之前then的註冊函數的返回值,並沒有打印出來。
* 也就是說 1 和 2 並沒有被打印出來,看下面的解釋
*
*/
function Promise(fn){
var status = 'pending'
var value= null;
var callbacks = [];
this.then = function(onFulfilled) {
if(status === 'pending'){
callbacks.push({f:onFulfilled});
return this;
}
onFulfilled(value);
return this;
}
function resolve(newValue){
value = newValue;
status = 'fulfilled';
setTimeout(function(){
callbacks.map(function(cb,index){
if(index === 0){
callbacks[index].value = newValue;
}
var rsp = cb.f(cb.value);
if(typeof callbacks[index+1] !== 'undefined'){
callbacks[index+1].value = rsp;
}
})
},0)
}
fn(resolve);
}
var p = new Promise(function(resolve){
resolve('aaaaaa')
})
p.then(function(response){
console.log(response);
return 1;
}).then(function(response){
console.log(response); // 這裏應當打印的是45行返回的1,然則打印出來的確是aaaaaa
return 2;
}).then(function(response){
console.log(response); // 這裏應當打印的是48行返回的2,然則打印出來的確是aaaaaa
})
setTimeout(function(){
p.then(function(response){
console.log('can i invoke?');
})
},1000)
/**
* 題目的泉源在於什麼呢?
* 題目的泉源是每次的then的返回值都是p,當狀況是fulfilled,實行的是onFulfilled(value)
* 此處的value是p的value,也就是fulfilled狀況的value。依據範例,promise應當是只能發射單值。
* 而我們設想了一個callback客棧中有一系列的值。生生的把promise變成了多值發射。
*
* 所以,調解思緒,每一個then都應當返回一個promise,這個promise應當是一個全新的promise。
* 詳細完成見下一個例子。
*/
/**
* 依據適才的剖析,我們從新優化一下代碼
* 1.去掉之前的多值設想
* 2.每次的then 返回的都是一個全新的promise
*
*/
function Promise(fn){
var status = 'pending'
var value= null;
var callbacks = [];
var self = this;
this.then = function(onFulfilled) {
return new Promise(function(resolve){
function handle(value){
var res = typeof onFulfilled === 'function' ? onFulfilled(value) : value;
resolve(res);
}
// 假如是pending狀況,則到場到註冊行列中去。
if(status === 'pending'){
callbacks.push(handle);
// 假如是fulfilled 狀況。
}else if(status === 'fulfilled'){
handle(value);
}
})
}
function resolve(newValue){
value = newValue;
status = 'fulfilled';
setTimeout(function(){
callbacks.map(function(cb){
cb(value);
})
},0)
};
fn(resolve);
}
//
var p = new Promise(function(resolve){
resolve('這是相應的數據')
})
p.then(function(response){
console.log(response);
return 1;
}).then(function(response){
console.log(response);
return 2;
}).then(function(response){
console.log(response);
})
setTimeout(function(){
p.then(function(response){
console.log('can i invoke?');
})
},1000)
/**
* 運轉一下,圓滿輸出
* 先是輸出“這是相應的數據”,然後是“1”,然後是“2”, 然後是“can i invoke?”
*
* 接下來我們要好好整頓一下代碼了。把一些公用的要領放到組織函數的原型上去。革新以後的例子見下一個例子
*/
/**
* 依據適才的剖析,我們從新優化一下代碼
* 1.把私有屬性掛到實例上去
* 2.把大眾要領掛到組織函數的原型上去
*
*/
function Promise(fn){
this.status = 'pending';
this.value= null;
this.callbacks = [];
var self = this;
function resolve(newValue){
self.value = newValue;
self.status = 'fulfilled';
setTimeout(function(){
self.callbacks.map(function(cb){
cb(value);
})
},0)
}
fn(resolve);
}
Promise.prototype = Object.create(null);
Promise.prototype.constructor = Promise;
Promise.prototype.then = function(onFulfilled){
var self = this;
return new Promise(function(resolve){
function handle(value){
var res = typeof onFulfilled === 'function'? onFulfilled(value) : value;
resolve(res);
}
if(self.status==='pending'){
self.callbacks.push(handle);
}else if(self.status ==='fulfilled'){
handle(self.value);
}
})
}
// 運用
var p = new Promise(function(resolve){
resolve('這是相應的數據')
})
p.then(function(response){
console.log(response);
return 1;
}).then(function(response){
console.log(response);
return 2;
}).then(function(response){
console.log(response);
})
setTimeout(function(){
p.then(function(response){
console.log('can i invoke?');
})
},1000)
5.處置懲罰註冊的函數返回值是promise的狀況
/**
* 不出預料,又要拋出題目了。當then註冊的回調函數返回的是promise的時刻,從這個then以後的一切then的註冊函數
* 都應當註冊在新返回的promise上。直到碰到下一個回調函數的返回值也是promise。
*
* 完成思緒:
* 在handle中推斷註冊函數返回的是不是是promise。假如是的話,則resolve這個返回的promise的值,詳細代碼看一下36到38行
*
*/
function Promise(fn){
this.status = 'pending';
this.value= null;
this.callbacks = [];
var self = this;
function resolve(newValue){
self.value = newValue;
self.status = 'fulfilled';
setTimeout(function(){
self.callbacks.map(function(cb){
cb(value);
})
},0)
}
fn(resolve);
}
Promise.prototype = Object.create(null);
Promise.prototype.constructor = Promise;
Promise.prototype.then = function(onFulfilled){
var self = this;
var promise = new Promise(function(resolve){
function handle(value){
var res = typeof onFulfilled === 'function'? onFulfilled(value) : value;
if(res instanceof Promise){
promise = res;
resolve(res.value);
}else {
resolve(res);
}
}
if(self.status==='pending'){
self.callbacks.push(handle);
}else if(self.status ==='fulfilled'){
handle(self.value);
}
})
return promise;
}
// 運用
var p = new Promise(function(resolve){
resolve('這是相應的數據')
})
p.then(function(response){
console.log(response);
return new Promise(function(resolve){
resolve('testtest')
})
}).then(function(response){
console.log(response);
return 2;
}).then(function(response){
console.log(response);
})
setTimeout(function(){
p.then(function(response){
console.log('can i invoke?');
return new Promise(function(resolve){
resolve('hhhhhh')
})
}).then(function(response){
console.log(response);
})
},1000)
源碼悉數在github上:https://github.com/JesseZhao1…