异步代码对于执行耗时的任务很有用,但是当然,它并非没有缺点 。 异步代码使用回调函数来处理其结果,但是回调函数无法返回典型JavaScript函数可以返回的值 。
因此,它们不仅剥夺了我们控制功能执行的能力,而且使错误处理变得有些麻烦。 这是Promise
对象出现的地方,因为它旨在填补异步编码中的一些缺陷 。
从技术上讲, Promise
是JavaScript中的标准内部对象 ,这意味着它内置于JavaScript中 。 它用于表示异步代码块的最终结果 (或代码失败的原因),并具有控制异步代码执行的方法。
句法
我们可以使用new
关键字创建Promise
对象的实例 :
new Promise(function(resolve, reject) {} );
作为参数传递给Promise()
构造函数的函数称为executor 。 它保存异步代码,并具有两个Function
类型的参数 ,称为resolve
和reject
函数(稍后将对此进行详细介绍)。
Promise
对象的初始状态称为未决 。 在这种状态下,异步计算的结果不存在 。
计算成功后 ,初始暂挂状态将变为已完成状态。 在这种状态下,计算结果可用。
万一异步计算失败 , Promise
对象将从其初始挂起状态移至拒绝状态。 在这种状态下,可以使计算失败的原因 (即错误消息)可用。
为了从待处理状态变为完成状态, 调用resolve()
。 为了从待处理状态变为拒绝状态,请调用reject()
。
当状态从未决变为完成时 ,将执行Promise
对象的then
方法的事件处理程序。 并且,当状态从未决变为拒绝时 ,将执行Promise
对象的catch
方法的事件处理程序。
例子1
“未承诺”代码
假设有一个hello.txt
文件,其中包含“ Hello”字样。 这是我们可以编写AJAX请求以获取该文件并显示其内容的方式 ,而无需使用Promise
对象:
function getTxt() {
let xhr = new XMLHttpRequest();
xhr.open('GET', 'hello.txt');
xhr.overrideMimeType('text/plain');
xhr.send();
xhr.onload = function() {
try {
switch (this.status) {
case 200:
document.write(this.response);
break;
case 404:
throw 'File Not Found';
default:
throw 'Failed to fetch the file';
}
} catch (err) {
console.log(err)
}
};
}
getTxt();
如果已成功获取文件的内容,即响应状态代码为200 ,则将响应文本写入文档中 。 如果找不到文件(状态404) ,则会引发“找不到文件”错误消息 。 否则,将抛出一条一般错误消息,指示获取文件失败。
“承诺”代码
现在,让我们承诺上面的代码 :
function getTxt() {
return new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open('GET', 'hello.txt');
xhr.overrideMimeType('text/plain');
xhr.send();
xhr.onload = function() {
switch (this.status) {
case 200:
resolve(this.response);
case 404:
reject('File Not Found');
default:
reject('Failed to fetch the file');
}
};
});
}
getTxt().then(
function(txt) {
document.write(txt);
}).catch(
function(err) {
console.log(err);
});
现在,对getTxt()
函数进行了编码,以返回Promise
对象的新实例 ,并且其执行程序函数保存以前的异步代码。
当响应状态代码为200时 ,通过调用resolve()
来实现 Promise
(响应作为resolve()
的参数传递)。 当状态代码是404或一些其它的Promise
使用 拒绝 reject()
具有作为参数的相应的错误消息reject()
当Promise
实现时 ,将运行then()
方法的处理程序。 它的参数是resolve()
传递的参数 。 在事件处理程序内部,将响应文本(作为参数接收) 写入文档 。
当Promise
被拒绝时 , catch()
方法的事件处理程序将运行,并记录error 。
上面的Promisified版本的代码的主要优点是错误处理 。 而不是像未承诺的版本那样抛出未捕获的异常,而是返回并记录适当的故障消息 。
但是,不仅返回 失败消息,而且返回 异步计算的结果,这对于我们来说确实是有利的。 为此,我们需要扩展示例。
例子2
“未承诺”代码
我不仅要显示来自hello.txt
的文本, hello.txt
希望将其与“世界”一词结合起来,并在 2秒钟的超时后将其显示在屏幕上。 这是我使用的代码:
function getTxt() {
let xhr = new XMLHttpRequest();
xhr.open('GET', 'hello.txt');
xhr.overrideMimeType('text/plain');
xhr.send();
xhr.onload = function() {
try {
switch (this.status) {
case 200:
document.write(concatTxt(this.response));
break;
case 404:
throw 'File Not Found';
default:
throw 'Failed to fetch the file';
}
} catch (err) {
console.log(err)
}
};
}
function concatTxt(res) {
setTimeout(function() {
return (res + 'World')
}, 2000);
}
getTxt();
在状态码200上, concatTxt()
函数将响应文本与“世界”字连接起来,然后再将其写入文档。
但是,此代码无法按需工作 。 setTimeout()
回调函数无法返回串联的字符串 。 未打印到文档的内容是undefined
因为那是concatTxt()
返回的内容 。
“承诺”代码
因此,为了使代码正常工作,让我们对上述代码进行 concatTxt()
,其中包括concatTxt()
:
function getTxt() {
return new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open('GET', 'hello.txt');
xhr.overrideMimeType('text/plain');
xhr.send();
xhr.onload = function() {
switch (this.status) {
case 200:
resolve(this.response);
case 404:
reject('File Not Found');
default:
reject('Failed to fetch the file');
}
};
});
}
function concatTxt(txt) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(txt + ' World');
}, 2000);
});
}
getTxt().then(
(txt) = > {
return concatTxt(txt);
}).then(
(txt) = > {
document.write(txt);
}).catch(
(err) = > {
console.log(err);
});
就像getTxt()
, concatTxt()
函数也返回一个新的Promise
对象,而不是串联的文本。 concatTxt()
返回的Promise
在setTimeout()
回调函数中解析 。
在上述代码的结尾附近,第一个then()
方法的事件处理程序在满足 getTxt()
的Promise
getTxt()
即成功获取文件getTxt()
时运行。 在该处理程序内, concatTxt()
并返回concatTxt()
返回的Promise
。
当满足 concatTxt()
返回的Promise
时concatTxt()
即两秒超时结束并且以连接字符串作为参数调用resolve()
时then()
,第二个then()
方法的事件处理程序将运行。
最后, catch()
捕获来自两个Promises的所有异常和失败消息 。
在此承诺版本中,“ Hello World”字符串将成功打印到文档中。
翻译自: https://www.hongkiat.com/blog/javascript-promise-object/