我正在通读promise
的内部运作this article。为此,作者展示了promise
的简化实现方式。
代码如下:
class PromiseSimple {
constructor(executionFunction) {
this.promiseChain = [];
this.handleError = () => {};
this.onResolve = this.onResolve.bind(this);
this.onReject = this.onReject.bind(this);
executionFunction(this.onResolve, this.onReject);
}
then(onResolve) {
this.promiseChain.push(onResolve);
return this;
}
catch(handleError) {
this.handleError = handleError;
return this;
}
onResolve(value) {
let storedValue = value;
try {
this.promiseChain.forEach((nextFunction) => {
storedValue = nextFunction(storedValue);
});
} catch (error) {
this.promiseChain = [];
this.onReject(error);
}
}
onReject(error) {
this.handleError(error);
}
}
就像常规的promise
一样被称为
// Assume this is your AJAX library. Almost all newer
// ones return a Promise Object
const makeApiCall = () => {
return new PromiseSimple((resolve, reject) => {
// Use a timeout to simulate the network delay waiting for the response.
// This is THE reason you use a promise. It waits for the API to respond
// and after received, it executes code in the `then()` blocks in order.
// If it executed is immediately, there would be no data.
setTimeout(() => {
const apiResponse = fakeApiBackend();
if (apiResponse.statusCode >= 400) {
reject(apiResponse);
} else {
resolve(apiResponse.data);
}
}, 5000);
});
};
我很难掌握以下内容:
为什么6
类的行7
和PromiseSimple
存在?我试图理解将this.onResolve
绑定到this
的意义。它是否已经绑定到正确的this
上下文?
我不明白为什么此实现不会阻塞主线程。 PromiseSimple
类中的任何地方都不会将工作转移到另一个线程或任何此类线程上。但是可以肯定的是,如果我在程序的末尾放置一个console.log(...)
语句,则该console.log(...)
语句将被打印为我期望的第一件事,就像常规的{{1 }}。我认为它会暂停直到伪造的promise
函数完成,因为这不是makeApiCall
的真实实现。
我想了解这一点,但是我只是不了解这个小型实现如何允许我们习惯的正确promise
行为。任何帮助将不胜感激。
更新
已经提出了将其设为duplicate的建议,但是我想详细说明为什么它们不一样。 duplicate question是关于为什么需要异步调用的更高级的理论。我了解它们的用途,重要性以及实现它们的各种方式。我想了解其幕后工作原理。
答案 0 :(得分:2)
onResolve
和onReject
必须绑定,以防止executionFunction
在其他上下文或任何上下文中应用它们。 (如果出于任何原因在另一个上下文中调用resolve
或reject
,它都必须绑定到PromiseSimple
,否则this
会引用其他内容或不会引用到没有上下文的任何东西)。 如果您不绑定onResolve
或onReject
,那么以下示例将不起作用:
const makeApiCall = () => {
return new PromiseSimple((resolve, reject) => {
Promise.resolve().then(resolve); // Using native promise here : resolve is called without context and won't work if not bound
});
}
then
或catch
回调中定义的任务作为事件循环中的微任务。 (关于浏览器事件循环,这很有意思article)。 希望这会有所帮助,
答案 1 :(得分:-1)
关于1,我们来看一个独立的示例
execute = func => func()
class X {
xfunc() {return this;}
}
let x = new X();
execute(x.xfunc) // undefined
execute(x.xfunc.bind(x)) // x
execute(x.xfunc.bind(5)) // 5
通常,当您引用诸如x.xfunc
之类的函数时,它失去了它的上下文。通常这不是问题,因为您通常不调用这样的对象函数,而是直接调用x.xfunc()
。所有对象都一样,不仅仅是使用class
和new
语法创建的对象。
let x = {xfunc: function() {return this}}
x.xfunc() // x
execute(x.xfunc) // window
execute(x.xfunc.bind(x)) // x
现在,有了箭头功能,情况有所不同
箭头函数表达式的语法比函数表达式短,并且没有自己的this,arguments,super或new.target MDN
let x = {xfunc: () => this}
x.xfunc() // window
execute(x.xfunc) // window
execute(x.xfunc.bind(x)) // window
关于2,它实际上只是回调。考虑下面的例子
let callback = () => { console.log('callback') }
let promise = { resolve: () => { callback() } }
console.log('this is not blocking');
现在,很明显,没有任何障碍,例如即使我们从未通过promise.resolve()
解决诺言,也会打印控制台日志。许诺实际上只是上面的语法糖,还有一些额外的链接逻辑。