起因
今天一个小程序客户接入我们的第三方js,没有正常执行,而我们自测是可以的。刚开始测试调节版本库发现大于等于1.9.2的就可以正常执行,低的就有问题。
debug
晚上在自己的环境上解除代码混淆,调试了一下发现是Promise的问题。
Promise.resolve(x).then(() => {
console.log(‘这里没有执行’)
})
看了下代码,由于项目中用到了defer对象,所以Promise是用的第三方而不是原生的。而该第三方Promise实现用到了setImmediate。webpack打包时,监测到代码中使用了setImmediate,就会自动加入setImmediate polifill实现。该setImmediate实现在一系列降级中回退到了setTimeout,并且用到了setTimeout的第三个参数,下面是使用setTimeout第三个参数的例子
setTimeout((x) => {
console.log(x)
}, 1000, '默认参数1')
在微信基础库版本1.9.2之前,这个参数没有被按标准实现。导致Promise出现异常。
解决方法
所以,最简单的方法就是在第三方Promise实现中去掉setImmediate的判断,直接用setTimeout(fn, 0)
或者,我们可以在webpack
配置中,使用下面配置来关闭自动注入的setImmediate
。
{
...
node: false
}
或者,直接用微信原生的Promise
,实现一个defer
就可以了。
Promise.defer = function () {
const deferred = {}
deferred.promise = new Promise(function (resolve, reject) {
deferred.resolve = resolve
deferred.reject = reject
})
return deferred
}
总结
最后,可以学习下webpack
的setImmediate
实现,依次降级用到了Process.nextTick(micro)、postMessage(macro)、MessageChannel(macro)、script(macro)、setTimeout(macro)
// Don't get fooled by e.g. browserify environments.
if ({}.toString.call(global.process) === "[object process]") {
// For Node.js before 0.9
installNextTickImplementation();
} else if (canUsePostMessage()) {
// For non-IE10 modern browsers
installPostMessageImplementation();
} else if (global.MessageChannel) {
// For web workers, where supported
installMessageChannelImplementation();
} else if (doc && "onreadystatechange" in doc.createElement("script")) {
// For IE 6–8
installReadyStateChangeImplementation();
} else {
// For older browsers
installSetTimeoutImplementation();
}
function installNextTickImplementation() {
registerImmediate = function(handle) {
process.nextTick(function () { runIfPresent(handle); });
};
}
function canUsePostMessage() {
// The test against `importScripts` prevents this implementation from being installed inside a web worker,
// where `global.postMessage` means something completely different and can't be used for this purpose.
if (global.postMessage && !global.importScripts) {
var postMessageIsAsynchronous = true;
var oldOnMessage = global.onmessage;
global.onmessage = function() {
postMessageIsAsynchronous = false;
};
global.postMessage("", "*");
global.onmessage = oldOnMessage;
return postMessageIsAsynchronous;
}
}
function installPostMessageImplementation() {
// Installs an event handler on `global` for the `message` event: see
// * https://developer.mozilla.org/en/DOM/window.postMessage
// * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
var messagePrefix = "setImmediate$" + Math.random() + "$";
var onGlobalMessage = function(event) {
if (event.source === global &&
typeof event.data === "string" &&
event.data.indexOf(messagePrefix) === 0) {
runIfPresent(+event.data.slice(messagePrefix.length));
}
};
if (global.addEventListener) {
global.addEventListener("message", onGlobalMessage, false);
} else {
global.attachEvent("onmessage", onGlobalMessage);
}
registerImmediate = function(handle) {
global.postMessage(messagePrefix + handle, "*");
};
}
function installMessageChannelImplementation() {
var channel = new MessageChannel();
channel.port1.onmessage = function(event) {
var handle = event.data;
runIfPresent(handle);
};
registerImmediate = function(handle) {
channel.port2.postMessage(handle);
};
}
function installReadyStateChangeImplementation() {
var html = doc.documentElement;
registerImmediate = function(handle) {
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
var script = doc.createElement("script");
script.onreadystatechange = function () {
runIfPresent(handle);
script.onreadystatechange = null;
html.removeChild(script);
script = null;
};
html.appendChild(script);
};
}
function installSetTimeoutImplementation() {
registerImmediate = function(handle) {
setTimeout(runIfPresent, 0, handle);
};
}
网友评论