Promise
JavaScript Promise (JavaScript承诺)
In JavaScript, a promise is a unique object linking the “producing code” and the “consuming code”. In other words, it is a “subscription list”. (在JavaScript中, promise是链接“生成代码”和“使用代码”的唯一对象。换句话说,它是一个“订阅列表”。)
The “producing code” takes the time it needs to produce the promised result, and the promise makes the result available to the overall subscribed code whenever it’s ready. (“生成代码”需要时间来生成承诺的结果,并且承诺在准备就绪时将结果提供给整个订阅代码。)
But, JavaScript promises are more complicated that the described simple subscription list: there are additional features and limitations inside them.
Let’s start at the very beginning. (讓 我們 從頭開始)
The constructor syntax for a promise object looks like this:
// load and execute the script at the given path
let promise = new Promise(function (resolve, reject) {
// executor (the producing code, "actor")
});
The function, which is passed to the new Promise, is named the executor. Whenever a new Promise is generated, the executor automatically runs. It involves the producing code that should eventually produce the result. The arguments of it, such as the resolve and reject, are considered callbacks provided by JavaScript. (传递给新Promise的函数被命名为executor。 每当生成新Promise时, executor都会自动运行。 它涉及最终应产生结果的生成代码。 它的参数(如resolve和reject )被视为JavaScript提供的回调。)
When the executor has the result, it must call one of the following callbacks:
resolve(value): in case the work is finished with result value.
reject(error): in case there was an error, the error is the error object.
The object promise returned by the new Promise constructor contains the following internal properties:
state: initially “pending”, then it changes either to “fulfilled” when you call resolve or “rejected” when you call reject.
result: it is initially undefined, then transforms into value when you call resolve(value) or resolve(value)when you call reject(error).
Now, let’s consider the example of a promise constructor along with a simple executor function with “producing code” taking time, using setTimeout:
let promise = new Promise(function (resolve, reject) {
// the function is executed automatically when creating the promise
(//创建promise时自动执行函数)
// after 1.5 second signal that the job is done with the result "done"
(//1.5秒后,发出作业已完成的信号,结果为“完成”)
setTimeout(() => resolve("done"), 1500);
});
Two things can be seen by running the code mentioned above:
The new Promise calls the executor at once and automatically. The executor gets two arguments: resolve and reject. The JavaScript engine pre-defines these functions. Hence you don’t have to create them. You should only call them when ready.
After “processing” one second, the executor calls resolve(“done”) for creating the result. (“处理”一秒钟后,执行者调用resolve (“done”)来创建结果。)
So, this was an example of successful job completion. (所以,这是一个成功完成工作的例子。)
Let’s check out an example containing an error:
let promise = new Promise(function (resolve, reject) {
resolve("done");
reject(new Error("…")); // ignored
setTimeout(() => resolve("…")); // ignored
});
let promise = new Promise(function (resolve, reject) {
// after 1.5 seconds the signal about the job is finished with an error
(//1.5秒后,有关作业的信号以错误结束)
setTimeout(() => reject(new Error("Error!!")), 1500);
});
As you can see, the reject(…) call moves the promise to “rejected”. (如您所见, reject (…)调用将promise移动到“rejected”。)
A promise, which is resolved or rejected, is known as “settled”. The executor might call only a single resolve or reject. Each state change is considered final. (已解决或已拒绝的承诺称为“已解决”。 执行者可能只调用一个解析或拒绝。 每次状态更改均被视为最终结果。)
Any further calls of either reject or resolve are ignored. (任何进一步的拒绝或解决调用都将被忽略。)
Take a look at the following example:
let promise = new Promise(function (resolve, reject) {
resolve("done");
reject(new Error("…")); // ignored
setTimeout(() => resolve("…")); // ignored
});
Also, note that reject and resolve expect a single argument, ignoring extra arguments. (此外,请注意, REJECT和RESOLVE需要单个参数,忽略多余的参数。)
Practically, an executor does something asynchronously, calling reject and resolve after some time. Also, it is possible to call resolve or reject immediately, like here:
let promise = new Promise(function (resolve, reject) {
resolve(1234); // immediately give the result: 1234
});
Consumers: then, catch, finally
Consumers: then, catch, finally
A promise object is a link between the executor and the consuming functions, that can receive a result or an error. You can register the consuming functions by using methods, such as then, .catch and .finally. (Promise对象是执行器和消费函数之间的链接,可以接收结果或错误。可以使用方法注册消费函数,例如then、.catch和.finally。)
Let’s examine them one by one. (让我们一个接一个地检查它们。)
then
then (然后)
.then is considered the most critical, fundamental method. (.then被认为是最关键、最基本的方法。)
Its syntax looks like this:
promise.then(
function (result) { /* a successful result */ },
function (error) { /* an error */ }
);
The number one argument of .then is a function, which runs whenever the promise is resolved, then receives the result. (.then的第一个参数是一个函数,每当promise被解析时运行,然后接收结果。)
The second argument is a function, which runs when the promise is rejected, receiving the error. (第二个参数是一个函数,在promise被拒绝时运行,收到错误。)
Here is an example of a successfully resolved promise reaction:
let promise = new Promise(function (resolve, reject) {
setTimeout(() => resolve("done!"), 1500);
});
// resolve runs the first function in .then
promise.then(
result => console.log(result), // shows "done!" after 1.5 second
(result = > console.log (result),//1.5秒后显示"done!")
error => console.log(error) // doesn't run
);
The following example visualizes the case of rejection:
let promise = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error("Error!!")), 1500);
});
// reject runs the second function in .then
promise.then(
result => console.log(result), // doesn't run
(result = > console.log (result),//不运行)
error => console.log(error) // shows "Error: Error!!" after 1.5 second
);
In case you are interested only in the successful completion, you may provide only one function argument, as follows:
let promise = new Promise(resolve => {
setTimeout(() => resolve("done"), 1500);
});
promise.then(console.log); // shows "done" after 1.5 second
catch
catch (v.捕 ,捕获,捕拿,逮住 ,擒拿,捉拿)
In case, you are interested in errors, you should use null as the first argument, like this: .then(null, errorHandlingFunction). Either you can use .catch(errorHandlingFunction), like this:
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Error!!")), 1500);
});
// .catch(f) is the same as promise.then(null, f)
promise.catch(console.log); // shows "Error: Error!!" after 1.5 second
So, the .catch(f) can be considered a shorthand of the .then(null, f). (因此, .catch (f)可以被认为是.then (null, f)的简写。)
finally
finally (最后)
The .finally(f) call in promises is almost the same as .then(f, f) in the sense that f runs whenever the promise is settled: no matter resolved or rejected.
It’s a handy method to perform cleanup: stop the loading indicators, when they are not necessary any longer.
The example looks like this:
new Promise((resolve, reject) => {
/* do something that takes time, and then call resolve/reject */
(/*做一些需要时间的事情,然后调用resolve/reject */)
})
// runs when the promise is settled, doesn't matter successfully or not
(//在promise结算时运行,是否成功无关紧要)
.finally(() => stop loading indicator)
(.finally (() = >停止加载指示灯))
.then(result => show result, err => show error)
Take into account that it’s not just an alias of then(f,f). The following differences can be outlined:
The finally handler doesn’t have any arguments. In it we don’t know the promise is successful or not. As a rule, its task is to finalize procedures. (- finally处理程序没有任何参数。在其中,我们不知道承诺是否成功。通常,其任务是最终确定程序。)
The finally handler is aimed at passing through results and errors to the next handler. (- finally处理程序旨在将结果和错误传递给下一个处理程序。)
Here is an example:
new Promise((resolve, reject) => {
setTimeout(() => resolve("result"), 2000)
(setTimeout (() = > resolve ("result"), 2000))
})
.finally(() => console.log("Promise ready"))
(.finally (() = > console.log ("承诺准备就绪")))
.then(result => console.log(result)); // .then handles the result
Also note that .finally(f) is a more convenient syntax than .then(f, f) as you don’t need to duplicate the function f (另请注意, .finally (f)是一个比.then (f, f)更方便的语法,因为您不需要复制函数f)
Another essential thing to know is that if a promise is pending .then/catch/finally handlers are waiting for it. But if a promise is already settled, they execute at once:
//the promise becomes resolved right after creation.
let promise = new Promise(resolve => resolve("done"));
promise.then(console.log); // done
Promises are quite flexible: you can add handlers anytime, and in case the result is there, they get it immediately.
Now, let’s get to more practical examples of how promises can help in writing asynchronous code. (现在,让我们来看看更多的实际示例,了解Promise如何帮助编写异步代码。)
Example: loadScript
Example: loadScript
As we already stated in the previous chapter, the loadScript function is used to load a script. (如前一章所述, loadScript函数用于加载脚本。)
This is an example of its usage in the callback:
function loadScript(src, callback) {
let createScript = document.createElement('script');
createScript.src = src;
createScript.onload = () => callback(null, createScript);
createScript.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(createScript);
}
If you want to rewrite it using the promises, you need to act as follows:
function loadScript(src) {
return new Promise(function (resolve, reject) {
let createScript = document.createElement('script');
createScript.src = src;
createScript.onload = () => resolve(createScript);
createScript.onerror = () => reject(new Error(`Script load error for ${src}`));
document.head.append(createScript);
});
}
As you can see in the above-given example, the new function loadScript doesn’t require a callback. Yet it creates and returns a promise object, which resolves when the loading is done. The outer code might add handlers to it applying .then. (正如您在上面给出的示例中看到的,新函数loadScript不需要回调。然而,它创建并返回一个promise对象,该对象在完成加载时解析。外部代码可能会向其添加应用.then的处理程序。)
The usage is the following:
function loadScript(src) {
return new Promise(function (resolve, reject) {
let createScript = document.createElement('script');
createScript.src = src;
createScript.onload = () => resolve(createScript);
createScript.onerror = () => reject(new Error(`Script load error for ${src}`));
document.head.append(createScript);
});
}
let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");
promise.then(
script => console.log(`${script.src} is loaded`),
error => console.log(`Error: ${error.message}`)
);
promise.then(script => console.log('Another handler'));