作者:印大大 | 来源:互联网 | 2023-01-15 13:48
我正在尝试用.then()
Javascript 实现具有可链接功能的简单promise类。这是我到目前为止所做的-
class APromise {
constructor(Fn) {
this.value = null;
Fn(resolved => { this.value = resolved; });
}
then(fn) {
fn(this.value);
return this;
}
}
function myFun() {
return new APromise(resolve => {
// just resolve('Hello'); works but this doesn't
setTimeout(() => { resolve('Hello'); }, 2000);
});
}
const log = v => { console.log(v); };
myFun().then(log).then(log);
输出-
null
null
Instead of 'Hello'
2 times. I think it is currently ignoring setTimeout()
call, how should I make this work?
1> zhirzh..:
问题
您的代码无法按照您想要的方式工作,因为您将异步流与同步流混合在一起。
当您调用时.then()
,它将this
同步返回。由于setTimeout()
是在一段时间(2秒)后调用的异步函数,因此this.value
它仍然是null
。
如果您想进一步了解JS的异步流程,建议您观看此视频。它有点长,但超级有帮助。
使您的代码正常工作
由于我们无法确定何时setTimeout()
将调用传递给它的函数,因此无法调用和回调依赖于其操作的函数。我们将这些回调存储在数组中,以备后用。
setTimeout()
调用该函数(承诺解决)时,我们得到承诺解决的结果。因此,我们现在调用所有绑定的回调。
class APromise {
constructor(Fn) {
this.value = null;
- Fn(resolved => { this.value = resolved; });
+ this.callbacks = [];
+ Fn(resolved => {
+ this.value = resolved;
+
+ this.callbacks.forEach(cb => {
+ cb(this.value);
+ });
+ });
}
then(fn) {
- fn(this.value);
+ this.callbacks.push(fn);
return this;
}
}
function myFun() {
return new APromise(resolve => {
setTimeout(() => { resolve('Hello'); }, 2000);
});
}
const log = v => { console.log(v); };
myFun().then(log).then(log);
连锁问题
上面的代码部分解决了该问题。
当一个回调的结果传递到下一个回调时,可以实现真正的链接。在当前代码中情况并非如此。为了做到这一点,每个人都.then(cb)
必须返回一个新值APromise
,该新值将在cb
调用函数时解析。
完整且符合Promises / A +的实现方式超出了单个SO答案的范围,但这不应给人以为它不可行的印象。这是精选的定制命令列表。
更全面的实施
让我们从一个干净的开始。我们需要一个Promise
实现方法的类,该方法then
还返回一个允许链接的承诺。
class Promise {
constructor(main) {
// ...
}
then(cb) {
// ...
}
}
这main
是一个以函数作为参数并在promise解析/实现时调用它的函数 -我们称此方法resolve()
。该功能resolve()
由我们的Promise
班级实现和提供。
function main(resolve) {
// ...
resolve(/* resolve value */);
}
该then()
方法的基本特征是cb()
,一旦诺言实现,就用诺言值触发/激活提供的回调函数。
考虑到这两点,我们可以重新连接我们的Promise
课程。
class Promise {
constructor(main) {
this.value = undefined;
this.callbacks = [];
const resolve = resolveValue => {
this.value = resolveValue;
this.triggerCallbacks();
};
main(resolve);
}
then(cb) {
this.callbacks.push(cb);
}
triggerCallbacks() {
this.callbacks.forEach(cb => {
cb(this.value);
});
}
}
我们可以使用tester()
函数测试当前代码。
(function tester() {
const p = new Promise(resolve => {
setTimeout(() => resolve(123), 1000);
});
const p1 = p.then(x => console.log(x));
const p2 = p.then(x => setTimeout(() => console.log(x), 1000));
})();
// 123
// 123
到此为止我们的基础。现在,我们可以实现链接。我们面临的最大问题是该then()
方法必须同步返回一个Promise ,它将异步解决。
我们需要等待父承诺解决,然后才能解决下一个承诺。这意味着,而不是添加cb()
到母公司的承诺,我们必须添加resolve()
的方法下一个诺言,它使用的返回值cb()
作为resolveValue
。
then(cb) {
- this.callbacks.push(cb);
+ const next = new Promise(resolve => {
+ this.callbacks.push(x => resolve(cb(x)));
+ });
+
+ return next;
}
如果这最后一点使您感到困惑,那么这里有一些提示:
Promise
构造函数接受函数main()
作为参数
main()
以函数resolve()
作为参数
resolve()
由Promise
构造函数提供
resolve()
接受任何类型的参数作为resolveValue
演示版
class Promise {
constructor(main) {
this.value = undefined;
this.callbacks = [];
const resolve = resolveValue => {
this.value = resolveValue;
this.triggerCallbacks();
};
main(resolve);
}
then(cb) {
const next = new Promise(resolve => {
this.callbacks.push(x => resolve(cb(x)));
});
return next;
}
triggerCallbacks() {
this.callbacks.forEach(cb => {
cb(this.value);
});
}
}
(function tester() {
const p = new Promise(resolve => {
setTimeout(() => resolve(123), 1000);
});
const p1 = p.then(x => console.log(x));
const p2 = p.then(x => setTimeout(() => console.log(x), 1000));
const p3 = p2.then(x => setTimeout(() => console.log(x), 100));
const p4 = p.then((x) => new Promise(resolve => {
setTimeout(() => resolve(x), 1000);
}))
/*
p: resolve after (1s) with resolveValue = 123
p1: resolve after (0s) after p resolved with resolveValue = undefined
p2: resolve after (0s) after p resolved with resolveValue = timeoutID
p3: resolve after (0s) after p2 resolved with resolveValue = timeoutID
p4: resolve after (1s) after p resolved with resolveValue = Promise instance
*/
})();
// 123
// 2
// 123