>  기사  >  웹 프론트엔드  >  JS의 Promise와 Async에 대한 자세한 설명

JS의 Promise와 Async에 대한 자세한 설명

Guanhui
Guanhui앞으로
2020-05-20 10:31:302589검색

JS의 Promise와 Async에 대한 자세한 설명

JavaScript는 단일 스레드 언어이기 때문에 동기 코드는 한 번에 한 줄만 실행할 수 있습니다. 즉, 동기화 코드를 실행하는 데 시간이 한 순간보다 오래 걸리면 완료될 때까지 나머지 코드 실행이 중지됩니다. 런타임이 불확실한 코드로 인해 다른 코드가 실행되지 않는 것을 방지하려면 비동기 코드를 사용해야 합니다.

Promise

이를 위해 코드에서 Promise를 사용할 수 있습니다. Promise는 불확실한 시간 동안 프로세스가 실행되고 그 결과가 성공 또는 실패일 수 있는 개체를 나타냅니다. JavaScript에서 Promise를 생성하려면 Promise 개체의 생성자를 사용하여 Promise를 생성합니다. Promise 생성자는 해결 및 거부 매개변수가 있는 실행 함수를 허용합니다. 두 매개변수 모두 약속 완료(성공적인 호출이 값을 반환함) 또는 거부(오류 값을 반환하고 약속을 실패로 표시함)를 콜백할 수 있는 함수이기도 합니다. 함수의 반환 값은 무시됩니다. 따라서 Promise는 Promise만 반환할 수 있습니다.

예를 들어 다음 코드와 같이 JavaScript에서 Promise를 정의합니다.

const promise = new Promise((resolve, reject) => {  
  setTimeout(() => resolve('abc'), 1000);  
});

위 코드는 1초 후에 abc를 반환하는 Promise를 생성합니다. 우리가 실행하는 setTimeout은 실행기 함수 내에서 1초 후에 abc를 반환하는 이행 약속이므로 비동기 코드입니다. setTimeout의 콜백 함수에서는 abc 값을 반환할 수 없으므로, 파싱된 값을 얻으려면 해결('abc')을 호출해야 합니다. then 함수를 사용하여 이행된 프로미스의 반환 값에 접근할 수 있습니다. then 함수는 이행된 약속의 반환 값을 매개변수로 사용하는 콜백 함수를 받아들입니다. 원하는 값을 얻을 수 있습니다. 예를 들어 다음과 같이 할 수 있습니다:

const promise = new Promise((resolve, reject) => {  
  setTimeout(() => resolve('abc'), 1000);  
});
promise.then((val) => {  
  console.log(val);  
})

위 코드를 실행하면 abc 레코드를 얻어야 합니다. 보시다시피 Promise는 완료 후 Resolve 함수가 호출될 때 값을 제공합니다.

Promise에는 세 가지 상태가 있습니다. 초기 상태에서는 약속이 성공하지도 실패하지도 않습니다. 완료 상태는 작업이 성공적으로 완료되었음을 의미합니다. 또는 실패 상태는 약속 작업이 실패했음을 의미합니다.

보류 중인 Promise는 값을 반환하여 이행하거나 오류로 인해 실패할 수 있습니다. Promise가 완료되면 then 함수는 해당 반환 값을 가져와 호출할 then 함수의 콜백 함수에 전달합니다. Promise가 실패하면 catch 함수를 사용하여 오류를 캡처할 수 있으며, 콜백 함수에 오류를 전달할 수도 있습니다. then과 catch는 둘 다 약속을 반환하므로 서로 연결될 수 있습니다.

예를 들어 다음과 같이 작성할 수 있습니다.

const promise = (num) => {  
  return new Promise((resolve, reject) => {  
    setTimeout(() => {  
      if (num === 1) {  
        resolve('resolved')  
      } else {  
        reject('rejected')  
      }  
    }, 1000);  
  });  
}
promise(1)  
  .then((val) => {  
    console.log(val);  
  })  
  .catch((error) => {  
    console.log(error);  
  })promise(2)  
  .then((val) => {  
    console.log(val);  
  })  
  .catch((error) => {  
    console.log(error);  
  })

위 코드에는 num이 1일 때 해결된 값으로 promise를 이행하고 num이 1이 아닌 경우 오류를 발생시키는 JavaScript promise를 반환하는 함수 promise가 있습니다. 커밋하다. 따라서 다음을 실행합니다:

promise(1)  
  .then((val) => {  
    console.log(val);  
  })  
  .catch((error) => {  
    console.log(error);  
  })

그런 다음 then 함수가 실행되고 num이 1이므로 promise(1) 함수 호출은 약속이 이행되었음을 반환하고 확인된 값을 val에서 사용할 수 있습니다. 따라서 console.log(val)을 실행하면 문제가 해결됩니다. 다음 코드를 실행하면:

promise(2)  
  .then((val) => {  
    console.log(val);  
  })  
  .catch((error) => {  
    console.log(error);  
  })

catch는 promise(2) 함수 호출이 promise를 반환하지 못했고 거부된 오류 값을 사용할 수 있고 error로 설정되었기 때문에 실행됩니다. 따라서 console.log(error)를 실행하면 거부된 출력이 표시됩니다.

JavaScript Promise 객체에는 길이와 프로토타입이라는 속성이 있습니다. length는 생성자 인수의 개수로, 1로 설정되고 항상 1입니다. 프로토타입 속성은 Promise 객체의 프로토타입을 나타냅니다.

promise에는 Promise의 완료 여부에 관계없이 코드를 실행하는 finally 메서드도 있습니다. finally 메소드는 콜백 함수를 매개변수로 받아들이고 실행하려는 코드를 실행할 수 있으며 Promise의 결과에 관계없이 실행될 수 있습니다. 예를 들어 다음과 같이 실행합니다.

Promise.reject('error')  
  .then((value) => {  
    console.log(value);  
  })  
  .catch((error) => {  
    console.log(error);  
  })  
  .finally(() => {  
    console.log('finally runs');  
  })

원래 Promise에 오류가 있어 거부되었기 때문에 오류가 발생하고 최종적으로 레코드를 실행합니다. 그런 다음 finally 메서드의 모든 코드를 실행합니다.

Promise 사용의 주요 이점은 비동기 코드를 작성할 때 Promise를 사용하여 순차적으로 실행할 수 있다는 것입니다. 이를 위해 then 함수를 사용하여 Promise를 연결할 수 있습니다. then 함수는 콜백 함수를 수신하고 Promise가 완료된 후 이를 실행합니다. 또한 Promise가 거부된 후 두 번째 매개변수도 허용합니다. 체인에서 Promise를 사용하려면 첫 번째 콜백 함수를 만든 다음 함수가 다른 Promise를 반환하도록 해야 합니다. 기존 Promise에 다른 Promise를 연결하고 싶지 않다면, 없음처럼 다른 값을 반환할 수 있습니다. 다음 then 함수에서 사용할 수 있는 값을 반환할 수 있습니다. 오류가 발생할 수도 있습니다. 그러면 그때까지 반환된 Promise는 오류와 함께 거부됩니다. 또한 이행되거나 거부된 Promise를 반환할 수 있으며, 그 뒤에 연결된 then 함수를 차단할 때 완료된 값을 얻거나, catch 함수의 콜백에서 오류 이유를 얻을 수 있습니다.

예를 들어 다음과 같이 쓸 수 있습니다.

Promise.resolve(1)  
  .then(val => {  
    console.log(val);  
    return Promise.resolve(2)  
  })  
  .then(val => {  
    console.log(val);  
  })Promise.resolve(1)  
  .then(val => {  
    console.log(val);  
    return Promise.reject('error')  
  })  
  .then(val => {  
    console.log(val);  
  })  
  .catch(error => console.log(error));Promise.resolve(1)  
  .then(val => {  
    console.log(val);  
    throw new Error('error');  
  })  
  .then(val => {  
    console.log(val);  
  })  
  .catch(error => console.log(error));

在第一个例子中,我们链式调用 promise,并且都 resolve 一个值。所有的 promise 都准备返回为值。在第二个和最后一个例子中,我们拒绝了第二个 promise 或抛出一个错误。它们都做了同样的事情。第二个 promise 被拒绝了,并且错误原因会被回调函数 catch 函数记录下来。我们还可以链接挂起状态的 promise,如下方代码所示:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 1000);  
});
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(2), 1000);  
});
promise1  
  .then(val => {  
    console.log(val);  
    return promise2;  
  })  
  .then(val => {  
    console.log(val);  
  })  
  .catch(error => console.log(error));

回调函数 then 函数返回了 promise2,这是一个挂起状态的 promise。

方法

JavaScript 的 promise 有以下方法。

Promise.all (可迭代对象)

Promise.all 接受一个可迭代对象,该对象允许我们在某些计算机上并行运行多个 promise,并在其他计算机上连续运行多个 promise。这对于运行多个不依赖于彼此的值的 promise 非常方便。它接受一个包含 promise 的列表 (通常是一个数组) 的可迭代对象,然后返回一个 Promise,这个 Promise 在可迭代对象中的 promise 被解析时解析。

例如,我们像下面这样写代码,使用 Promise.all 来运行多个 promise:

const promise1 = Promise.resolve(1);  
const promise2 = 2;  
const promise3 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(3), 1000);  
});  
Promise.all([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  });

如果我们运行了上方的代码,然后 console.log 应该会记录下 [1,2,3]。如我们所见,只有所有的 promise 都完成后返回它的解析值。如果其中的拒绝了,我们不会得到任何解析值。相反,我们将得到由被拒绝的 promise 返回的任何错误值。它将会在第一个被拒绝的 promise 处停止,并且发送值给回调函数 catch 函数。例如,如果我们这样:

const promise1 = Promise.resolve(1);  
const promise2 = Promise.reject(2);  
const promise3 = new Promise((resolve, reject) => {  
  setTimeout(() => reject(3), 1000);  
});  
Promise.all([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  })  
  .catch(error => {  
    console.log(error);  
  });

然后我们可以在回调函数 catch 函数的 console.log 中得到两个记录。

Promise.allSettled

Promise.allSettled 返回一个 promise,该 promise 的解析在所有的 promise 解析完或拒绝后。它接受带有一组 promise 的可迭代对象,例如,一个 promise 数组。返回的 promise 的解析值是每个 promise 的最终状态的数组。例如,假设我们有:

const promise1 = Promise.resolve(1);  
const promise2 = Promise.reject(2);  
const promise3 = new Promise((resolve, reject) => {  
  setTimeout(() => reject(3), 1000);  
});  
Promise.allSettled([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  })

若我们运行上方代码,我们将获得一个包含三个条目的数组,每个条目都是一个对象,该对象有已经完成 promise 的 status 和 value 属性以及被拒绝的 promise 的 status 和 reason 属性。例如,上面的代码会记录 {status: “fulfilled”, value: 1},{status: “rejected”, reason: 2},{status: “rejected”, reason: 3}。 fulfilled 状态记录的是成功的 promise,rejected 状态为被拒绝的 promise。

Promise.race

Promise.race 方法返回一个 promise,该 promise 会解析首先完成的 promise 的解析值。它接受一个带有 promise 集合的可迭代对象,例如,一个 promise 数组。如果传入的可迭代对象为空,则返回的 promise 将一直挂起。若可迭代对象包含一个或多个非 promise 值或者已经完成的 promise,Promise.race 将会返回这些条目中的第一个。例如,我们有:

const promise1 = Promise.resolve(1);  
const promise2 = Promise.resolve(2);  
const promise3 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(3), 1000);  
});  
Promise.race([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  })

然后我们看到 1 会被记录 。那是因为 promise1 是第一个被解析的,那是因为它是在下一行运行之前就被解析了。同样,如果我们的数组中有一个非 promise 值作为参数进行传入,如下代码所示:

const promise1 = 1;  
const promise2 = Promise.resolve(2);  
const promise3 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(3), 1000);  
});  
Promise.race([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  })

然后我们会得到相同的记录,因为它是我们传递给 Promise.race 方法的数组中的非 promise 值。同步代码始终运行在异步代码之前,而不论同步代码在哪里。如果我们有:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 2000);  
});
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(2), 1000);  
});
const promise3 = 3;  
Promise.race([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  })

然后我们记录下 3,因为 setTimeout 将回调函数放入队列中以便稍后运行,所以它将比同步代码更晚执行。

最后,如果我们有:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 2000);  
}); 
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(2), 1000);  
});
Promise.race([promise1, promise2])  
  .then((values) => {  
    console.log(values);  
  })

然后我们在控制台中得到记录 2,因为在一秒解析的 promise 要比两秒解析的 promise 更早解析。

Promise.reject

Promise.reject 返回一个因某种原因拒绝的 promise。拒绝带有 Error 的实例对象的 promise 非常有用。例如,如果我们有以下代码:

Promise.reject(new Error('rejected'))  
  .then((value) => {  
    console.log(value);  
  })  
  .catch((error) => {  
    console.log(error);  
  })

然后我们得到 rejected 记录。

Promise.resolve

Promise.resolve 返回一个已解析为传入 resolve 函数参数的值的 promise。我们也可以传递一个带有 then 属性的对象,它的值是 promise 的回调函数。如果该值具有 then 方法,则将使用 then 函数完成 promise。也就是说,then 函数值的第一个参数与 resolve 相同,以及第二个参数与 reject 相同。例如,我们可以编写如下代码:

Promise.resolve(1)  
  .then((value) => {  
    console.log(value);  
  })

然后我们得到 1 记录,因为 1 是我们传递给 resolve 函数来返后具有解析值 1 的承诺的值。

如果我们传入的对象内部带有 then 方法,如下代码所示:

Promise.resolve({  
    then(resolve, reject) {  
      resolve(1);  
    }  
  })  
  .then((value) => {  
    console.log(value);  
  })

然后我们得到记录的值 1。这是因为 Promise.resolve 函数将运行 then 函数,设置为 “then” 属性的函数的 “resolve” 参数将被假定为承诺中称为 “resolve” 函数的函数。并将该函数的 resolve 参数设置为 then 属性可以看作 promise 中一个叫做 resolve 函数。如果我们将传入 then 中的对象替换为 inject 函数,然后我们就可以得到被拒绝的 promise。代码如下所示:

Promise.resolve({  
    then(resolve, reject) {  
      reject('error');  
    }  
  })  
  .then((value) => {  
    console.log(value);  
  })  
  .catch((error) => {  
    console.log(error);  
  })

在上面的代码中,我们会得到 error 记录,这是因为 promise 被拒绝了。

Async 和 Await

使用 async 和 await,我们可以缩短 promise 代码。使用 async 和 await 之前前,我们必须得用 then 函数并且在 then 函数中放入回调函数作为所有的参数。这就使得我们有很多 promise 时代码冗长至极。相反,我们可以使用 async 和 await 语法来替代 then 函数以及相关回调。例如,我们可将以下代码缩短为:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 2000);  
});
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(2), 1000);  
});
promise1  
  .then((val1) => {  
    console.log(val1);  
    return promise2;  
  })  
  .then((val2) => {  
    console.log(val2);  
  })

写成:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 2000);  
});  
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(2), 1000);  
});
(async () => {  
  const val1 = await promise1;  
  console.log(val1)  
  const val2 = await promise2;  
  console.log(val2)  
})()

我们使用 await 来替换 then 和回调函数。然后,我们就可以将每个 promise 的解析值分配为变量。注意,如果我们为 promise 代码使用 await,那么我们必须像上例那样添加 async 到函数签名中。为了捕获错误,我们使用 catch 子句取代链式 catch 函数。另外,我们没有在底部链式调用 finally 函数以在 promise 结束时运行代码,而是在 catch 子句后使用 finally 子句。

例如,我们可以这样写:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 2000);  
});  
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => reject('error'), 1000);  
});
(async () => {  
  try {  
    const val1 = await promise1;  
    console.log(val1)  
    const val2 = await promise2;  
    console.log(val2)  
  } catch (error) {  
    console.log(error)  
  } finally {  
    console.log('finally runs');  
  }})()

在上面的代码中,我们获得了分配给变量的 promise 的解析值,而不是在 then 函数的回调中获取值,例如在 const response = await promise1 上面的一行。另外,我们使用 try...catch...finally 块来捕获被拒绝的 promise 的错误,以及 finally 子句替代 finally 函数,其无论 promise 执行结果如何,该代码都可以运行。

想其他使用 promise 的函数一样,async 函数始终返回 promise,并且不能返回其他任何东西。在上面的示例中,我们证明了与使用带有回调函数作为参数传递的 then 函数相比,我们可以更短的方式使用 promise。

结束语

使用 promise,让我们编写异步代码更容易。promise 是表示一个处理的运行时间不确定并且结果会成功也会失败的对象。在 JavaScript 中创建一个 promises,我们使用 Promise 对象,该对象是用于创建 promise 的构造函数。

Promise 构造函数接受一个拥有 resolve 和 reject 参数的执行函数。两个参数都是函数,它们让我们可以回调 promise 完成(成功调用得到返回值)或者拒绝(返回错误值并标记 promise 失败)。 The 函数的返回值被忽略。因此,promise 只能返回 promise。

因为 promise 返回 promise,所以 promise 是可链式调用。promise 的 then 函数可以抛出一个错误,返回解析值,或者其他 promise(挂起、已完成或已拒绝的 promise)。

推荐教程:《PHP教程

위 내용은 JS의 Promise와 Async에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 learnku.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제