module.exports = Promise;
var PENDING = undefined,
FULFILLED = 1,
REJECTED = 2;
function Promise(_resolver, name) {
debugger;
if (!isFunction(_resolver)) {
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}
if (!(this instanceof Promise)) {
// 當作函式用的話
return new Promise(_resolver);
}
this._name = name || 'then';
this.guid = Promise.prototype.guid++;
/* ---------------------------------- */
var promise = this;
promise._value;
promise._reason;
promise._status = PENDING;
/* ---------------------------------- */
promise._resolves = [];
promise._rejects = [];
/* ---------------------------------- */
debugger;
// 執行賦予的主任務
_resolver(resolve, reject);
return;
/* ---------------------------------- */
function resolve(value) {
debugger;
transition.apply(promise, [FULFILLED, value]);
}
function reject(reason) {
debugger;
transition.apply(promise, [REJECTED, reason]);
}
};
/* ========================================================================== */
Promise.prototype.guid = 0;
/* ========================================================================== */
Promise.prototype.then = function(onFulfilled, onRejected) {
debugger;
var promise = this;
// 每次返回一个promise,保证是可thenable的
return new Promise(function(resolve, reject) {
debugger;
if (promise._status === PENDING) {
promise._resolves.push(callback);
promise._rejects.push(errback);
} else if (promise._status === FULFILLED) { // 状态改变后的then操作,立刻执行
callback(promise._value);
} else if (promise._status === REJECTED) {
errback(promise._reason);
}
return;
/* ---------------------------------------- */
function callback(value) {
debugger;
// 執行(onFulfilled)
var ret = isFunction(onFulfilled) && onFulfilled(value) || value;
if (isThenable(ret)) {
ret.then(function(value) {
resolve(value);
}, function(reason) {
reject(reason);
});
} else {
resolve(ret);
}
}
/* ---------------------------------------- */
function errback(reason) {
debugger;
reason = isFunction(onRejected) && onRejected(reason) || reason;
reject(reason);
}
});
};
/* ========================================================================== */
Promise.prototype.catch = function(onRejected) {
return this.then(undefined, onRejected)
};
/* ========================================================================== */
Promise.prototype.delay = function(ms, val) {
return this.then(function(ori) {
return Promise.delay(ms, val || ori);
})
};
/* ========================================================================== */
Promise.delay = function(ms, val) {
return Promise(function(resolve, reject) {
setTimeout(function() {
resolve(val);
}, ms);
})
};
/* ========================================================================== */
Promise.resolve = function(arg) {
return Promise(function(resolve, reject) {
resolve(arg)
})
};
/* ========================================================================== */
Promise.reject = function(arg) {
return Promise(function(resolve, reject) {
reject(arg)
})
};
/* ========================================================================== */
Promise.all = function(promises) {
if (!isArray(promises)) {
throw new TypeError('You must pass an array to all.');
}
return Promise(function(resolve, reject) {
var i = 0,
result = [],
len = promises.length,
count = len
function resolver(index) {
return function(value) {
resolveAll(index, value);
};
}
function rejecter(reason) {
reject(reason);
}
function resolveAll(index, value) {
result[index] = value;
if (--count == 0) {
resolve(result)
}
}
for (; i < len; i++) {
promises[i].then(resolver(i), rejecter);
}
});
};
/* ========================================================================== */
Promise.race = function(promises) {
if (!isArray(promises)) {
throw new TypeError('You must pass an array to race.');
}
return Promise(function(resolve, reject) {
var i = 0,
len = promises.length;
function resolver(value) {
resolve(value);
}
function rejecter(reason) {
reject(reason);
}
for (; i < len; i++) {
promises[i].then(resolver, rejecter);
}
});
};
////////////////////////////////////////////////////////////////////////////////
function isFunction(obj) {
return 'function' === typeof obj;
};
/* ========================================================================== */
function isArray(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
};
/* ========================================================================== */
/**
* 若對象含有.then()
*/
function isThenable(obj) {
return obj && typeof obj['then'] == 'function';
};
/* ========================================================================== */
function transition(status, value) {
debugger;
var promise = this;
if (promise._status !== PENDING) {
return;
}
console.log('------------------');
console.log('name = %s, guid = %d', this._name, this.guid);
console.dir(this._resolves);
console.dir(this._rejects);
console.log('------------------');
// 所以的执行都是异步调用,保证then是先执行的
// 存疑的步驟
setTimeout(function() {
debugger;
console.log('do mission: name = %s, guid = %d', promise._name, promise.guid);
promise._status = status;
// 執行任務
publish.call(promise, value);
}, 0);
};
/* ========================================================================== */
/**
* 執行(._resolves)or(_rejects)
*/
function publish(val) {
debugger;
var promise = this,
fn,
st = promise._status === FULFILLED;
// 任務隊列
var queue = promise[st ? '_resolves' : '_rejects'];
while (fn = queue.shift()) {
val = fn.call(promise, val) || val;
}
promise[st ? '_value' : '_reason'] = val;
promise['_resolves'] = undefined;
promise['_rejects'] = undefined;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
教你一步步實現一個Promise
Promise我想現在大家都非常熟悉了,主要作用就是解決異步回調問題,這裡簡單介紹下。
Promise規範是CommonJS規範之一,而Promise規範又分了好多種,比如 Promises/A、Promises/B、Promises/Kiss等等
現在比較流行的是Promise/A規範,人們對它的完善和擴展,逐漸形成了Promise/A+規範,A+已脫穎而出。
說到這裡規範是什麼,可以去這裡瞭解下
現在已有瀏覽器內置支持Promise,它的api語法可以在這裡查看
可以看到它的api並不多,其實規範也不多,我覺的大致抓住幾個重要的點就夠了,
1、promise有三種狀態,等待(pending)、已完成(fulfilled)、已拒絕(rejected)
2、promise的狀態只能從「等待」轉到「完成」或者「拒絕」,不能逆向轉換,同時「完成」和「拒絕」也不能相互轉換
3、promise必須有一個then方法,而且要返回一個promise,供then的鏈式調用,也就是可thenable的
4、then接受倆個回調(成功與拒絕),在相應的狀態轉變時觸發,回調可返回promise,等待此promise被resolved後,繼續觸發then鏈
知道這幾個重要的特點,我們就可以參考瀏覽器內置的api來實現了,
我們可以不必太受規範約束,先按照自己的想法來就好了。
promise的使用大致如下
var promise = new Promise(function(resolve, reject) {
setTimeout(function(){
resolve("val")
});
});
promise.then(onFulfilled,onRejected).then(onFulfilled,onRejected)
主要思路就是我們可以直接對返回的promise對象進行操作,比如then,傳入回調,
這裡的函數並不會立即執行,而是加入隊列,等待未來的某個時間resolve時被觸發執行。
有了以上說明,就可以來實現了
首先定義三個狀態
var PENDING = undefined, FULFILLED = 1, REJECTED = 2;
然後實現Promise構造函數,此函數接受一個函數參數,函數參數接受倆個我們提供的方法resolve與reject,
供使用者在未來的某個時間裡調用觸發執行我們的隊列,這裡還要初始下當前的狀態,傳遞的值,
以及then時保存到的隊列。
大概像下面這樣
var Promise = function(resolver){
if (!isFunction(resolver))
throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");
if(!(this instanceof Promise)) return new Promise(resolver);
var promise = this;
promise._value;
promise._reason;
promise._status = PENDING;
promise._resolves = [];
promise._rejects = [];
var resolve = function(value){
}
var reject = function(reason){
}
resolver(resolve,reject);
}
有了這個,我們在實現一個then就ok了,
then裡要做的就是返回一個promise供then的鏈式調用,
而且promise.then(onFulfilled,onRejected)時,我們要判斷當前promise的狀態,
如果是pending則把onFulfilled與onRejected添加到_resolves與_rejects數組裡,
否則的話根據狀態,直接觸發回調,這裡要注意的是,如果返回的是promise,我們要等到此promise被resolves時,觸發then鏈的下一個promise執行。
代碼大概是這樣
Promise.prototype.then = function(onFulfilled,onRejected){
var promise = this;
return Promise(function(resolve,reject){
function callback(value){
var ret = isFunction(onFulfilled) && onFulfilled(value) || value;
if(isThenable(ret)){
ret.then(function(value){
resolve(value);
},function(reason){
reject(reason);
});
}else{
resolve(ret);
}
}
function errback(reason){
reason = isFunction(onRejected) && onRejected(reason) || reason;
reject(reason);
}
if(promise._status === PENDING){
promise._resolves.push(callback);
promise._rejects.push(errback);
}else if(promise._status === FULFILLED){
callback(promise._value);
}else if(promise._status === REJECTED){
errback(promise._reason);
}
});
}
這裡說明下
var isThenable = function(obj){
return obj && typeof obj["then"] == "function";
}
也就是說返回的對象帶有then方法,我們就當作promise對象
到這裡我們主要的工作就完成了,其他的all,race等方法都很簡單,具體可以到這裡看完整的實現
下面我們來做幾個例子來看下效果
var getData100 = function(){
return Promise(function(resolve,reject){
setTimeout(function(){
resolve("100ms");
},100);
});
}
var getData200 = function(){
return Promise(function(resolve,reject){
setTimeout(function(){
resolve("200ms");
},200);
});
}
getData100().then(function(data){
console.log(data);
return getData200();
}).then(function(data){
console.log(data);
return data + data;
}).then(function(data){
console.log(data)
});
當然可以直接getData100().then(getData200).then(function(val){})
then可以只傳一個,接受成功的回調,也可以用catch方法,接受失敗的回調,
catch是then的一個語法糖,相當於promise.then(undefined, onRejected)
也可以用all來並行執行
Promise.all([getData100(),getData200()]).then(function(value){
console.log(value)
});
結果的順序與傳入的順序相同。
我們也可以直接創建一個以obj為值的成功狀態的promise,
Promise.resolve("FULFILLED").then(function(val){
console.log(val)
});
實現都相當簡單,看代碼就懂。
這裡也可以做一些好玩的,比如創建一個delay方法
Promise.prototype.delay = function(ms){
return this.then(function(val){
return Promise.delay(ms,val);
})
}
Promise.delay = function(ms,val){
return Promise(function(resolve,reject){
setTimeout(function(){
resolve(val);
},ms);
})
}
我們可以每隔多少毫秒執行一些操作
Promise.delay(1000).then(function(){
}).delay(1000).then(function(){
})
我們也可以包裝一個循環,執行多少次,每次延遲多少秒執行什麼操作
var len = 0,
words = "你好,你是誰?";
function count(num,ms,cb){
var pro = Promise.resolve();
for (var i = 0; i < num; i++) {
pro = pro.delay(ms).then(function(v){
return cb(v);
});
};
}
count(words.length,800,function(){
var w = words.substr(0,++len);
console.log(w);
})
更多的東西等你來實現~