我正在兑现自己的JavaScript承诺。

我答应你一个奇迹

有时候,当您真的想了解某些东西时,值得尝试自己构建它。 这就是我对本文的承诺所做的。 我正在尝试建立一个非常基本的“承诺”。

最初有回调...

让我们从基础开始,在任何人听到诺言之前,我们都要进行回调。 看一下下面的代码片段。

const testUrl = ' https://api.github.com/users/odemeulder '
const fetchData = (url, callback) => {
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState == 200 ) {
callback(xhr.responseText)
}
}
xhr.open(url)
xhr.send()
}
fetchData(testUrl, response => console.log(response) )

它有什么作用? 它创建了一个非常简单的函数 ,该函数接受一个url,使用XMLHttpRequest对其进行读取,并允许回调函数对结果进行处理。 在最后一行,我们调用新函数; 作为回调,我们提供了一个输出响应的函数。 很简单。 如果您不熟悉XMLHttpRequest ,请不要着急,对本文的其余部分进行理解并不重要。 只需知道这是浏览器提供给JavaScript的对象即可执行http请求。 它是一个异步函数,当onreadystatechange事件触发时,它将进行回调。

一个诺言看起来像什么?

在开始写我们自己的承诺之前,让我们看看最终结果应该是什么样。 什么是通话签名?

function fetchDataReturningPromise(url) {
// what happens here?
}
function functionWithDelay(arg) {
// what happens here?
}
fetchDataReturningPromise(url).then(response => console.log(response))
// or
functionWithDelay(arg1).then(performFollowUpOperation)
// or
functionWithDelay(arg1).then((resolvedValue) =>
PerformFollowUpOperation(resolvedValue))

我们有什么在这里? 让我们关注第三个例子。

我们有一个延迟的功能; 函数内部的某些内容将进行异步调用。 可能是超时,http请求,数据库调用。

该函数返回“某物”。 某物具有then方法,因此“某物”必须是一个对象。 实际上,“某物”将是一个功能。

then函数采用一个参数,形式为函数或回调。 直到functionWithDelay完成后,传递给then函数的回调才会执行。 在我们的第三个例子中,我们传递给函数then标记performFollowUpOperation 请注意, performFollowUpOperation一个参数( resolvedValue ),并且该参数由functionWithDelay提供。 很多东西要在这里打开。 暂停,然后重新阅读上一段(如果您愿意)。

综上所述:

  1. 用下一个功能定义一个功能。

2.定义一个具有延迟的函数,该函数将返回带有下一个函数的函数( functionWithDelay

3.具有next功能的功能需要采用另一个功能作为参数。 在完成具有延迟的功能之前,无法执行该后续功能。 performFollowUpOperation

4.延迟功能必须以某种方式将值传递给后续功能。

所以现在我们需要填写functionWithDelay ,首先我们需要定义具有next功能的东西。

第一次天真尝试

让我们尝试一下。

 1: function FunctionWithAThenFunction() {
2: this.then = function (followUpFunction) {
3: followUpFunction()
4: }
5: }
 6: function functionWithDelay(param1) {
7: let xhr = new XMLHttpRequest()
8: xhr.onreadystatechange = () => {
9: if (xhr.readyState == 200) callback(xhr.responseText)
10: }
11: xhr.open(param1)
12: xhr.send()
13: return new FunctionWithAThenFunction()
14: }

好的,我们有一个带有then函数的函数。 我们的functionWithDelay返回带有then函数的函数。 但。 第9行的callback是什么? 以某种方式, followUpFunction应该最终出现在回调中。 那没有发生。 因此, followUpFunction将立即执行,而不会等到functionWithDelay完成。

我们如何使followUpFunction成为callback

第二次尝试

让我们看一下functionWithDelay 延迟功能的特点是什么? 或异步功能的特点是什么? 他们通常会以某种方式进行回调。 因此,让我们尝试将该函数重写为接受回调,以便我们可以轻松访问该回调。

然后,让我们重写FunctionWithAThenFunction ,让它接受一个函数,更具体地说是一个带有回调的异步函数。

1: function FunctionWithAThenFunction(aFnWithDelayAndACallback){
2: let nextThingToDo
3: this.then = function ( followUpFunction) {
4: nextThingToDo = followUpFunction
5: }
6: aFnWithDelayAndACallback( nextThingToDo )
7: }
8: function functionWithDelay(param1) {
9: const httpRequestWithCallback = function (callback) {
10: let xhr = XMLHttpRequest()
11: xhr.onreadystatechange = () => {
12: if (xhr.readyState == 200) callback(xhr.responseText)
13: }
14: xhr.open(param1)
15: xhr.send()
16: }
17: return new FunctionWithAThenFunction(httpRequestWithCallback)
18: }
19: functionWithDelay('some argument').then(console.log)

第1-7行定义了FunctionWithAThenFunction 该函数将延迟返回该函数。 它期望函数作为参数。 更具体地说,是带有回调的函数。 then函数将回调设置为局部变量( nextThingToDo )。 该函数确实做一件事,那就是使用回调函数调用该函数,在我们的示例中,该函数标记为aFnWithDelayAndACallback

第8-18行定义了我们的functionWithDelay 在我们的示例中,该函数执行http请求并使用then函数返回某些内容。 而且带有then函数的东西是FunctionWithAThenFunction的新实例。 它看起来与我们上面所做的有些不同。 我们创建一个名为httpRequestWithCallback的新本地临时函数,该函数接受一个回调参数,并包装前面示例中的整个XMLHttpRequest代码。 然后将该新的局部函数作为参数传递给FunctionWithAThenFunction

现在,如果您尝试运行此代码(第19行),它将仍然无法完成我们正在尝试执行的操作。 如果继续进行nextThingToDo ,您将发现在调用它之前, nextThingToDoundefined因为我们有机会为其分配值。 我们缺少一件事,那就是某种地位。

第三次尝试

让我们添加一个变量来跟踪执行状态。

1: function FunctionWithAThenFunction(aFnWithDelayAndACallback){
2: let nextThingToDo
3: let status = 'pending'
4: this.then = function ( followUpFunction) {
5: if (status === 'pending') {
6: nextThingToDo = followUpFunction
7: }
8: else {
9: followUpFunction()
10: }
11: }
12: aFnWithDelayAndACallback( (newValue) => {
13: status = 'resolved'
14: nextThingToDo(newValue)
15: })
16: }
17: function functionWithDelay(param1) {
18: const httpRequestWithCallback = function (callback) {
19: let xhr = XMLHttpRequest()
20: xhr.onreadystatechange = () => {
21: if (xhr.readyState == 200) callback(xhr.responseText)
22: }
23: xhr.open(param1)
24: xhr.send()
25: }
26: return new FunctionWithAThenFunction(httpRequestWithCallback)
27: }
28: functionWithDelay('some argument').then(console.log)

您会看到我们声明了一个status变量(第3行)。 这样,我们涵盖了所有基础。 如果then主要功能是完成之前被调用,我们存储followUpFunction在一个变量,如果不是我们可以放心地执行它。 但是它没有主函数的值。 让我们修复它。

1: function FunctionWithAThenFunction(aFnWithDelayAndACallback){
2: let nextThingToDo, value
3: let status = 'pending'
4: this.then = function ( followUpFunction) {
5: if (status === 'pending') {
6: nextThingToDo = followUpFunction
7: }
8: else {
9: followUpFunction(value)
10: }
11: }
12: aFnWithDelayAndACallback( (newValue) => {
13: status = 'resolved'
14: value = newValue
15: nextThingToDo(newValue)
16: })
17: }
18: function functionWithDelay(param1) {
19: const httpRequestWithCallback = function (callback) {
20: let xhr = XMLHttpRequest()
21: xhr.onreadystatechange = () => {
22: if (xhr.readyState == 200) callback(xhr.responseText)
23: }
24: xhr.open(param1)
25: xhr.send()
26: }
27: return new FunctionWithAThenFunction(httpRequestWithCallback)
28: }
29: functionWithDelay('some argument').then(console.log)

在这里,我们声明了一个名为value的新局部变量。 (第2行)在解析主函数时,将设置该值。 因此可以将其作为参数传递给第9行的followUpFunction

说到解决,为清楚起见,让我们重新编写一下。 与以前的示例功能相同。

1: function FunctionWithAThenFunction(aFnWithDelayAndACallback){
2: let nextThingToDo, value
3: let status = 'pending'
4: this.then = function ( followUpFunction) {
5: if (status === 'pending') {
6: nextThingToDo = followUpFunction
7: }
8: else {
9: followUpFunction(value)
10: }
11: }
12: const resolve = newValue => {
13: value = newValue
14: status = 'resolved'
15: if (nextThingToDo) nextThingToDo(newValue)
16: }
17: aFnWithDelayAndACallback(resolve)
17: }
18: function functionWithDelay(param1) {
19: const httpRequestWithCallback = function (callback) {
20: let xhr = XMLHttpRequest()
21: xhr.onreadystatechange = () => {
22: if (xhr.readyState == 200) callback(xhr.responseText)
23: }
24: xhr.open(param1)
25: xhr.send()
26: }
27: return new FunctionWithAThenFunction(httpRequestWithCallback)
28: }
29: functionWithDelay('some argument').then(console.log)

在这里,为了清楚起见,在第12-16行中引入了本地解析函数。 随后在第17行调用它。

放在一起

我们已经走了很长一段路。 让我们再重写一次最后一个示例,然后稍微更改一下函数名称。

  • FunctionWithAThenFunction成为Promise
  • functionWithDelay变为fetchData
  • httpRequestWithCallback变为httpRequest
  • aFnWithDelayAndACallback fn
function Promise(fn){
let nextThingToDo, value
let status = 'pending'
this.then = function ( callback ) {
if (status === 'pending') {
nextThingToDo = callback
}
else {
callback(value)
}
}
const resolve = newValue => {
value = newValue
status = 'resolved'
if (nextThingToDo) nextThingToDo(newValue)
}
fn(resolve)
}
function fetchData(url) {
const httpRequest = function (callback) {
let xhr = XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState == 200) callback(xhr.responseText)
}
xhr.open(url)
xhr.send()
}
return new Promise(httpRequest)
}
const testUrl = ' https://api.github.com/users/odemeulder '
fetchData(testUrl).then(console.log)

Promise代码现在可以在其他功能中使用。 假设您希望有一个类似于setTimeout的函数,该函数返回了Promise。 让我们调用函数Wait ,它将接受延迟时间(以毫秒为单位)。

function Wait(milliSeconds) {
const fn = (callback) => setTimeout(callback, milliSeconds)
return new Promise(fn)
}
Wait(5000).then(() => console.log('we waited 5 seconds'))

或假设您要在Node中使用httpRequest示例。 在Node XmlHttpRequest由于该浏览器提供了一个对象,因此无法使用,您必须使用Node的内置https模块。

const https = require('https')
const testUrl = ' https://api.github.com/users/odemeulder '
function fetchDataForNode(url) {
const fn = callback => {
const options = {
url: url,
headers: { 'User-Agent': 'request' }
}
https.get(options, response => {
let ret
response.on('data', chunk => ret += chunk)
response.on('end', () => callback(ret))
})
return new Promise(fn)
}
fetchDataForNode(testUrl).then(console.log)

同样,不要担心https.get在节点中如何工作的技术https.get 重要的部分是:创建一个接受回调并包装异步调用的函数,然后将该函数传递给Promise构造函数。

下一步

显然,诺言比我们刚刚概述的要多得多。 这篇文章已经足够长了。 但是这里有一些改进/补充。

一件事是then函数应该返回一个新的promise。 这使您可以兑现承诺。 doSomething().then(somethingElse).then(aThirdThing)

我们没有涉及的另一大问题是错误处理。 我们的Promise函数还应该实现catch函数。 该函数将一个函数作为参数,并且在发生任何错误的情况下将调用该函数。

结论

我们创建了一个非常基本的承诺对象,我向您展示如何创建一个函数的三个例子,它返回一个承诺( fetchData对于浏览器的HTTP请求, fetchDataForNode在节点和HTTP请求wait )。 在每种情况下,您都将创建一个接受回调的函数,创建一个新的Promise函数并将该函数作为回调传递。

我欢迎任何反馈和掌声。

From: https://hackernoon.com/i-promise-you-a-miracle-396301751c4b

相关文章:

  • 2018-06-11
  • 2020-07-13
  • 2019-02-23
  • 2021-12-16
  • 2019-11-28
  • 2021-12-14
  • 2021-10-19
  • 2021-11-17
猜你喜欢
  • 2021-10-01
  • 2021-10-19
  • 2021-10-17
  • 2021-08-24
  • 2021-11-14
  • 2021-09-17
  • 2021-10-19
  • 2020-07-29
相关资源
相似解决方案