Files
App-Installer-For-Windows-8…/shared/html/js/promise.js
2026-04-04 19:27:45 +08:00

534 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// <reference path="//Microsoft.WinJS.2.0/js/base.js" />
(function(global) {
"use strict";
/**
* PromisePolyfill 构造函数。
*
* 模拟 WinJS.Promise 的构造形式:
* new PromisePolyfill(init, oncancel)
*
* @constructor
* @param {function(function(any):void,function(any):void,function(any):void):void} pfInit
* Promise 初始化函数。
* 该函数在 Promise 创建时立即执行,并接收三个回调:
* - complete(value) : 完成 Promise
* - error(reason) : 使 Promise 失败
* - progress(value) : 发送进度通知(当前实现为占位)
*
* @param {function():void} [pfOnCancel]
* Promise 取消回调。当调用 promise.cancel() 时执行。
*
* @example
* var p = new PromisePolyfill(
* function (complete, error, progress) {
* setTimeout(function () {
* complete("done");
* }, 1000);
* },
* function () {
* console.log("Promise canceled");
* }
* );
*/
function PromisePolyfill(pfInit, pfOnCancel) {
/// <param name="pfInit" type="Function">
/// Promise 初始化函数。
/// 形参签名:
/// function(
/// complete : function(any):void,
/// error : function(any):void,
/// progress : function(any):void
/// )
/// </param>
/// <param name="pfOnCancel" type="Function" optional="true">
/// Promise 取消回调函数。当 promise.cancel() 被调用时执行。
/// </param>
var swState = "pending"; // "fulfilled" | "rejected"
var vValue = undefined;
var aHandlers = [];
function invokeHandlers() {
if (swState === "pending") return;
for (var i = 0; i < aHandlers.length; i++) {
handle(aHandlers[i]);
}
aHandlers = [];
}
function handle(hHandler) {
if (swState === "pending") {
aHandlers.push(hHandler);
return;
}
var pfCallback = swState === "fulfilled" ?
hHandler.onFulfilled :
hHandler.onRejected;
if (!pfCallback) {
if (swState === "fulfilled") {
hHandler.resolve(vValue);
} else {
hHandler.reject(vValue);
}
return;
}
try {
var vResult = pfCallback(vValue);
hHandler.resolve(vResult);
} catch (ex) {
hHandler.reject(ex);
}
}
function resolve(vResult) {
try {
if (vResult === self)
throw new TypeError("A promise cannot be resolved with itself.");
if (vResult && (typeof vResult === "object" || typeof vResult === "function")) {
var pfThen = vResult.then;
if (typeof pfThen === "function") {
pfThen.call(vResult, resolve, reject);
return;
}
}
swState = "fulfilled";
vValue = vResult;
invokeHandlers();
} catch (ex) {
reject(ex);
}
}
function reject(vReason) {
swState = "rejected";
vValue = vReason;
if (typeof PromisePolyfill.onerror === "function") {
PromisePolyfill.onerror(vReason);
}
invokeHandlers();
}
// WinJS Promise progress当前仅占位
function progress(vProgress) {
// 当前 polyfill 未实现 progress 传播
}
var self = this;
try {
if (typeof pfInit === "function") {
pfInit(resolve, reject, progress);
}
} catch (ex) {
reject(ex);
}
this.then = function(pfOnFulfilled, pfOnRejected) {
return new PromisePolyfill(function(resolve, reject) {
handle({
onFulfilled: pfOnFulfilled,
onRejected: pfOnRejected,
resolve: resolve,
reject: reject
});
});
};
this["catch"] = function(pfOnRejected) {
return this.then(null, pfOnRejected);
};
this.done = function(pfOnFulfilled, pfOnRejected) {
this.then(pfOnFulfilled, pfOnRejected)["catch"](function(ex) {
setTimeout(function() {
throw ex;
}, 0);
});
};
this.cancel = function() {
if (pfOnCancel) {
try {
pfOnCancel();
} catch (ex) {}
}
reject(new Error("Promise was canceled"));
};
this._oncancel = pfOnCancel;
this._state = swState;
this._value = vValue;
}
/**
* 检查对象是否为 PromisePolyfill 实例
* @param {any} vObj 待检查对象
* @returns {boolean} 是否为 PromisePolyfill 实例
*/
PromisePolyfill.is = function(vObj) {
return vObj instanceof PromisePolyfill;
};
/**
* 创建一个已完成的 PromisePolyfill
* @param {any} vValue 要返回的值
* @returns {PromisePolyfill} 已完成的 PromisePolyfill
*/
PromisePolyfill.resolve = function(vValue) {
return new PromisePolyfill(function(resolve) { resolve(vValue); });
};
/**
* 创建一个已拒绝的 PromisePolyfill
* @param {any} vReason 拒绝原因
* @returns {PromisePolyfill} 已拒绝的 PromisePolyfill
*/
PromisePolyfill.reject = function(vReason) {
return new PromisePolyfill(function(resolve, reject) { reject(vReason); });
};
/**
* 等待所有 Promise 完成
* @param {Array<PromisePolyfill|any>} aPromises 待处理的 Promise 或普通值数组
* @returns {PromisePolyfill<Array<any>>} 返回包含所有结果的 Promise
*/
PromisePolyfill.all = function(aPromises) {
return new PromisePolyfill(function(resolve, reject) {
var nRemaining = aPromises.length;
var aResults = new Array(nRemaining);
if (nRemaining === 0) resolve([]);
function resolver(iIndex) {
return function(vValue) {
aResults[iIndex] = vValue;
nRemaining--;
if (nRemaining === 0) resolve(aResults);
};
}
for (var i = 0; i < aPromises.length; i++) {
PromisePolyfill.resolve(aPromises[i]).then(resolver(i), reject);
}
});
};
/**
* 竞速 Promise谁先完成就返回谁的结果
* @param {Array<PromisePolyfill|any>} aPromises 待处理的 Promise 或普通值数组
* @returns {PromisePolyfill<any>} 最先完成的 Promise 的值
*/
PromisePolyfill.race = function(aPromises) {
return new PromisePolyfill(function(resolve, reject) {
for (var i = 0; i < aPromises.length; i++) {
PromisePolyfill.resolve(aPromises[i]).then(resolve, reject);
}
});
};
/**
* Promise join同 all
* @param {Array<PromisePolyfill|any>} aPromises 待处理的 Promise 或普通值数组
* @returns {PromisePolyfill<Array<any>>} 返回包含所有结果的 Promise
*/
PromisePolyfill.join = function(aPromises) {
return PromisePolyfill.all(aPromises);
};
/**
* 任意 Promise 完成即返回
* @param {Array<PromisePolyfill|any>} aPromises 待处理的 Promise 或普通值数组
* @returns {PromisePolyfill<any>} 最先完成的 Promise 的值,若都失败则 reject 一个错误数组
*/
PromisePolyfill.any = function(aPromises) {
return new PromisePolyfill(function(resolve, reject) {
var nRemaining = aPromises.length;
var aErrors = new Array(nRemaining);
if (nRemaining === 0) reject(new Error("No promises provided."));
function resolver(vValue) { resolve(vValue); }
function rejecter(iIndex) {
return function(ex) {
aErrors[iIndex] = ex;
nRemaining--;
if (nRemaining === 0) reject(aErrors);
};
}
for (var i = 0; i < aPromises.length; i++) {
PromisePolyfill.resolve(aPromises[i]).then(resolver, rejecter(i));
}
});
};
/**
* 给 Promise 添加超时处理
* @param {PromisePolyfill|any} pPromise 要处理的 Promise
* @param {number} nMilliseconds 超时时间(毫秒)
* @returns {PromisePolyfill<any>} 超时或原 Promise 完成后 resolve/reject
*/
PromisePolyfill.timeout = function(pPromise, nMilliseconds) {
return new PromisePolyfill(function(resolve, reject) {
var hTimer = setTimeout(function() {
reject(new Error("Promise timed out after " + nMilliseconds + "ms"));
}, nMilliseconds);
PromisePolyfill.resolve(pPromise).then(function(vValue) {
clearTimeout(hTimer);
resolve(vValue);
}, function(ex) {
clearTimeout(hTimer);
reject(ex);
});
});
};
PromisePolyfill.as = function(vValue) {
return PromisePolyfill.resolve(vValue);
};
PromisePolyfill.wrap = function(vValue) {
return PromisePolyfill.resolve(vValue);
};
PromisePolyfill.wrapError = function(vError) {
return PromisePolyfill.reject(vError);
};
/**
* 将数组的每个值依次执行回调
* @param {Array<any>} aValues 数组
* @param {function(any, number): any | PromisePolyfill<any>} pfCallback 回调函数
* @returns {PromisePolyfill<Array<any>>} 所有回调完成的结果数组
*/
PromisePolyfill.thenEach = function(aValues, pfCallback) {
var aPromises = [];
for (var i = 0; i < aValues.length; i++) {
aPromises.push(PromisePolyfill.resolve(aValues[i]).then(pfCallback));
}
return PromisePolyfill.all(aPromises);
};
var hListeners = {};
/**
* 全局事件注册
* @param {string} sType 事件类型
* @param {function(any):void} pfHandler 回调函数
*/
PromisePolyfill.addEventListener = function(sType, pfHandler) {
if (!hListeners[sType]) hListeners[sType] = [];
hListeners[sType].push(pfHandler);
};
/**
* 全局事件移除
* @param {string} sType 事件类型
* @param {function(any):void} pfHandler 回调函数
*/
PromisePolyfill.removeEventListener = function(sType, pfHandler) {
if (!hListeners[sType]) return;
var aList = hListeners[sType];
for (var i = 0; i < aList.length; i++) {
if (aList[i] === pfHandler) {
aList.splice(i, 1);
break;
}
}
};
/**
* 全局事件派发
* @param {string} sType 事件类型
* @param {any} vDetail 事件详情
*/
PromisePolyfill.dispatchEvent = function(sType, vDetail) {
if (!hListeners[sType]) return;
var aList = hListeners[sType].slice();
for (var i = 0; i < aList.length; i++) {
try { aList[i](vDetail); } catch (ex) {}
}
};
PromisePolyfill.supportedForProcessing = true;
PromisePolyfill.onerror = null;
/**
* 创建一个在指定毫秒数后完成的 Promise。
*
* @param {number} nMilliseconds
* 延迟时间(毫秒)。
*
* @returns {PromisePolyfill}
* 返回 Promise在延迟结束后完成。
*
* @example
* WinJS.Promise.delay(500).then(function () {
* console.log("500ms elapsed");
* });
*/
PromisePolyfill.delay = function(nMilliseconds) {
/// <param name="nMilliseconds" type="Number">
/// 延迟时间(毫秒)。
/// </param>
/// <returns type="PromisePolyfill"/>
var hTimer = null;
return new PromisePolyfill(
function(complete, error, progress) {
hTimer = setTimeout(function() {
complete();
}, nMilliseconds);
},
function() {
if (hTimer !== null) {
clearTimeout(hTimer);
hTimer = null;
}
}
);
};
/**
* 创建一个循环执行的 Promise类似 setInterval。
*
* 该 Promise 不会自动完成,除非:
* 1. 调用 promise.cancel()
* 2. callback 抛出异常
*
* @param {function(): (any|PromisePolyfill|WinJS.Promise)} pfCallback
* 每次循环执行的回调函数。可以返回 Promise。
*
* @param {number} nDelay
* 每次执行之间的间隔时间(毫秒)。
*
* @returns {PromisePolyfill}
* 返回 Promise 对象,可通过 cancel() 停止循环。
*
* @example
* var p = WinJS.Promise.interval(function () {
* console.log("tick");
* }, 1000);
*
* setTimeout(function () {
* p.cancel();
* }, 5000);
*/
PromisePolyfill.interval = function(pfCallback, nDelay) {
/// <param name="pfCallback" type="Function">
/// 每次间隔执行的函数。可以返回 Promise。
/// </param>
/// <param name="nDelay" type="Number">
/// 执行间隔(毫秒)。
/// </param>
/// <returns type="PromisePolyfill"/>
var bCanceled = false;
return new PromisePolyfill(
function(complete, error, progress) {
function loop() {
if (bCanceled) {
complete();
return;
}
try {
var vResult = pfCallback();
if (vResult && typeof vResult.then === "function") {
vResult.then(waitNext, error);
} else {
waitNext();
}
} catch (ex) {
error(ex);
}
}
function waitNext() {
if (bCanceled) {
complete();
return;
}
setTimeout(loop, nDelay);
}
loop();
},
function() {
bCanceled = true;
}
);
};
if (typeof global.Promise !== "undefined") {
global.Promise.delay = PromisePolyfill.delay;
global.Promise.interval = PromisePolyfill.interval;
}
if (typeof global.WinJS !== "undefined" && typeof global.WinJS.Promise !== "undefined") {
global.WinJS.Promise.delay = PromisePolyfill.delay;
global.WinJS.Promise.interval = PromisePolyfill.interval;
}
if (typeof global.Promise !== "undefined") {
var p = global.Promise;
if (!p.join) p.join = p.all;
if (!p.any) p.any = PromisePolyfill.any;
if (!p.timeout) p.timeout = PromisePolyfill.timeout;
if (!p.as) p.as = p.resolve;
if (!p.wrap) p.wrap = p.resolve;
if (!p.wrapError) p.wrapError = p.reject;
if (!p.thenEach) p.thenEach = PromisePolyfill.thenEach;
if (!p.is) p.is = function(vObj) { return vObj instanceof p; };
if (!p.supportedForProcessing) p.supportedForProcessing = true;
if (!p.addEventListener) p.addEventListener = PromisePolyfill.addEventListener;
if (!p.removeEventListener) p.removeEventListener = PromisePolyfill.removeEventListener;
if (!p.dispatchEvent) p.dispatchEvent = PromisePolyfill.dispatchEvent;
if (!p.onerror) p.onerror = null;
if (typeof p.prototype.then !== "function") {
p.prototype.then = function(onFulfilled, onRejected) {
return new p(function(resolve, reject) {
this.then(resolve, reject);
}).then(onFulfilled, onRejected);
};
}
if (typeof p.prototype.done !== "function") {
p.prototype.done = function(onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)["catch"](function(ex) {
setTimeout(function() {
throw ex;
}, 0);
});
};
}
}
if (typeof global.WinJS !== "undefined" && typeof global.WinJS.Promise !== "undefined") {
var wp = global.WinJS.Promise;
if (!wp.resolve) wp.resolve = function(vValue) { return new wp(function(c) { c(vValue); }); };
if (!wp.reject) wp.reject = function(vReason) { return new wp(function(c, e) { e(vReason); }); };
if (!wp.all) wp.all = function(aPromises) { return wp.join(aPromises); };
if (!wp.race) wp.race = PromisePolyfill.race;
global.Promise = wp;
if (typeof global.Promise === "undefined") global.Promise = wp;
}
if (typeof global.Promise === "undefined" && typeof global.WinJS === "undefined") {
global.Promise = PromisePolyfill;
}
})(this);