ES6 - Promise

Promise 语法

下面提到了与 promise 相关的语法,其中 p 是 promise 对象,resolve 是 promise 成功执行时应调用的函数,reject 是 promise 遇到错误时应调用的函数。

let p = new Promise(function(resolve,reject){
   let workDone = true; // 一些耗时的工作
      if(workDone){
      //调用解析函数已传递
      
	  resolve('success promise completed')
   }
   else{
      reject('ERROR , work could not be completed')
   }
})

示例

下面给出的示例展示了一个函数 add_positivenos_async(),它异步添加两个数字。如果传递了正值,则Promise得到解决。如果传递了负值,则Promise被拒绝。

<script>   
   function add_positivenos_async(n1, n2) {
      let p = new Promise(function (resolve, reject) {
         if (n1 >= 0 && n2 >= 0) {
            //做一些复杂耗时的工作
            resolve(n1 + n2)
         }
         else
            reject('NOT_Postive_Number_Passed') 
         })
         return p;
   }

   add_positivenos_async(10, 20)
      .then(successHandler) // if promise resolved
      .catch(errorHandler);// if promise rejected

   add_positivenos_async(-10, -20)
      .then(successHandler) // if promise resolved
      .catch(errorHandler);// if promise rejected

   function errorHandler(err) {
      console.log('Handling error', err)
   }
   function successHandler(result) {
      console.log('Handling success', result)
   }

   console.log('end')
</script> 

上述代码的输出将如下所示 −

end
Handling success 30
Handling error NOT_Postive_Number_Passed

Promises 链式执行

Promises 链式执行可用于需要依次执行一系列异步任务的情况。当一个 Promises 依赖于另一个 Promises 的结果时,Promises 链式执行。如下面的示例所示

示例

在下面的示例中,add_positivenos_async() 函数异步添加两个数字,如果传递了负值,则拒绝。当前异步函数调用的结果作为参数传递给后续函数调用。注意,每个 then() 方法都有一个 return 语句。

<script>   
   function add_positivenos_async(n1, n2) {
      let p = new Promise(function (resolve, reject) {
         if (n1 >= 0 && n2 >= 0) {
            //做一些复杂耗时的工作
            resolve(n1 + n2)
         }
         else
            reject('NOT_Postive_Number_Passed')
      })
      return p;
   }

   add_positivenos_async(10,20)
   .then(function(result){
      console.log("first result",result)
      return add_positivenos_async(result,result)
   }).then(function(result){
   console.log("second result",result)
      return add_positivenos_async(result,result)
   }).then(function(result){
      console.log("third result",result)
   })

   console.log('end')
</script> 

上述代码的输出将如下所示 −

end
first result 30
second result 60
third result 120

下面详细讨论了promise对象的一些常用方法−

promise.all()

此方法可用于聚合多个promise的结果。

语法

下面提到了promise.all()方法的语法,其中,iterable是一个可迭代对象。例如数组。

Promise.all(iterable);

示例

下面给出的示例执行异步操作数组[add_positivenos_async(10,20),add_positivenos_async(30,40),add_positivenos_async(50,60)]。当所有操作完成后,Promise就完全解决了。

<script>   
   function add_positivenos_async(n1, n2) {
      let p = new Promise(function (resolve, reject) {
         if (n1 >= 0 && n2 >= 0) {
            //做一些复杂耗时的工作
            resolve(n1 + n2)
         }
         else
            reject('NOT_Postive_Number_Passed')
      })

      return p;
   }
   //Promise.all(iterable)

Promise.all([add_positivenos_async(10,20),add_positivenos_async(30,40),add_positivenos_async(50,60)])
   .then(function(resolveValue){
      console.log(resolveValue[0])
      console.log(resolveValue[1])
      console.log(resolveValue[2])
      console.log('all add operations done')
   })
   .catch(function(err){
      console.log('Error',err)
   })
   console.log('end')
</script> 

上述代码的输出将如下所示 −

end
30
70
110
all add operations done

promise.race()

此函数接受一个Promise数组并返回第一个已解决的Promise。

语法

下面提到了promise.race()函数的语法,其中,iterable 是一个可迭代对象。例如数组。

Promise.race(iterable)

示例

下面给出的示例采用异步操作数组[add_positivenos_async(10,20),add_positivenos_async(30,40)]

只要任何一个添加操作完成,Promise就会得到解决。Promise不会等待其他异步操作完成。

<script>   
   function add_positivenos_async(n1, n2) {
      let p = new Promise(function (resolve, reject) {
         if (n1 >= 0 && n2 >= 0) {
            //做一些复杂耗时的工作
            resolve(n1 + n2)
         } else
            reject('NOT_Postive_Number_Passed')
      })

      return p;
   }

   //Promise.race(iterable)
   Promise.race([add_positivenos_async(10,20),add_positivenos_async(30,40)])
   .then(function(resolveValue){
      console.log('one of them is done')
      console.log(resolveValue)
   }).catch(function(err){
      console.log("Error",err)
   })

   console.log('end')
</script> 

上述代码的输出将如下所示 −

end
one of them is done
30

Promises 是在 JavaScript 中实现异步编程的一种简洁方法(ES6 新功能)。在 Promises 之前,回调用于实现异步编程。让我们首先了解什么是异步编程及其使用回调的实现。

了解回调

函数可以作为参数传递给另一个函数。这种机制称为 回调。回调在事件中很有用。

以下示例将帮助我们更好地理解这个概念。

<script>   
   function notifyAll(fnSms, fnEmail) {   
      console.log('starting notification process');   
      fnSms();   
      fnEmail();   
   }   
   notifyAll(function() {   
      console.log("Sms send ..");   
   }, 
   function() {   
      console.log("email send ..");   
   });   
   console.log("End of script"); 
   //executes last or blocked by other methods   
</script> 

在上面显示的 notifyAll() 方法中,通知通过发送短信和发送电子邮件进行。因此,notifyAll 方法的调用者必须传递两个函数作为参数。每个函数承担一个职责,如发送短信和发送电子邮件。

成功执行上述代码后将显示以下输出。

starting notification process 
Sms send .. 
Email send .. 
End of script 

在上面提到的代码中,函数调用是同步的。这意味着 UI 线程将等待完成整个通知过程。同步调用变为阻塞调用。现在让我们了解非阻塞或异步调用。

了解 AsyncCallback

考虑上面的例子。

要启用脚本,请执行对 notifyAll() 方法的异步或非阻塞调用。我们将使用 JavaScript 的 setTimeout() 方法。此方法默认是异步的。

setTimeout() 方法采用两个参数 −

  • 回调函数。

  • 调用该方法后的秒数。

在这种情况下,通知过程已使用超时包装。因此,它将需要两秒钟的延迟,由代码设置。将调用 notifyAll(),主线程继续执行其他方法。因此,通知过程不会阻止主 JavaScript 线程。

<script>   
   function notifyAll(fnSms, fnEmail) {   
      setTimeout(function() {   
         console.log('starting notification process');   
         fnSms();   
         fnEmail();   
      }, 2000);   
   }   
   notifyAll(function() {   
      console.log("Sms send ..");   
   },  
   function() {   
      console.log("email send ..");   
   });   
   console.log("End of script"); //executes first or not blocked by others   
</script>

成功执行上述代码后将显示以下输出。

End of script 
starting notification process 
Sms send .. 
Email send .. 

如果有多个回调,代码会看起来很吓人。

<script>   
   setTimeout(function() {   
      console.log("one");   
      setTimeout(function() {   
         console.log("two");   
         setTimeout(function() {   
            console.log("three");   
         }, 1000);   
      }, 1000);   
   }, 1000);   
</script>

ES6 通过引入Promise的概念来拯救你。Promise是"延续事件",它们帮助你以更简洁的代码风格一起执行多个异步操作。

示例

让我们通过一个例子来理解这一点。以下是相同的语法。

var promise = new Promise(function(resolve , reject) {    
   // do a thing, possibly async , then..  
   if(/*everthing turned out fine */)    resolve("stuff worked");  
   else     
   reject(Error("It broke"));  
});  
return promise;
// Give this to someone

实现Promise的第一步是创建一个使用Promise的方法。假设在这个例子中,getSum()方法是异步的,即其操作不应阻止其他方法的执行。一旦此操作完成,它将稍后通知调用者。

以下示例(步骤 1)声明了一个 Promise 对象"var promise"。Promise 构造函数首先调用函数以成功完成工作,然后调用另一个函数以防发生错误。

Promise通过使用 resolve 回调并传入结果(即 n1+n2)来返回计算结果

步骤 1 − resolve(n1 + n2);

如果 getSum() 遇到错误或意外情况,它将调用 Promise 中的拒绝回调方法,并将错误信息传递给调用者。

步骤 2 − accept(Error("Negatives not supports"));

方法实现在下面的代码中给出(步骤 1)。

function getSum(n1, n2) {   
   varisAnyNegative = function() {   
      return n1 < 0 || n2 < 0;   
   }   
   var promise = new Promise(function(resolve, reject) {   
      if (isAnyNegative()) {   
         reject(Error("Negatives not supported"));   
      }   
      resolve(n1 + n2)
   });   
   return promise;   
} 

第二步详细说明了调用者的实现(步骤 2)。

调用者应使用"then"方法,该方法采用两种回调方法 - 第一种用于成功,第二种用于失败。每种方法都采用一个参数,如以下代码所示。

getSum(5, 6)   
.then(function (result) {   
   console.log(result);   
},   
function (error) {   
   console.log(error);   
});

成功执行上述代码后将显示以下输出。

11 

由于 getSum() 的返回类型是 Promise,我们实际上可以有多个"then"语句。第一个"then"将有一个 return 语句。

getSum(5, 6)   
.then(function(result) {   
   console.log(result);   
   returngetSum(10, 20); 
   // this returns another promise   
},   
function(error) {   
   console.log(error);   
})   
.then(function(result) {   
   console.log(result);   
}, 
function(error) {   
   console.log(error);
});    

成功执行上述代码后将显示以下输出。

11
30

以下示例使用 getSum() 方法发出三个 then() 调用。

<script>   
   function getSum(n1, n2) {   
      varisAnyNegative = function() {   
         return n1 < 0 || n2 < 0;   
      }   
      var promise = new Promise(function(resolve, reject) {   
         if (isAnyNegative()) {   
            reject(Error("Negatives not supported"));   
         }   
         resolve(n1 + n2);   
      });   
      return promise;   
   }   
   getSum(5, 6)   
   .then(function(result) {   
      console.log(result);   
      returngetSum(10, 20); 
      //this returns another Promise   
   },   
   function(error) {   
      console.log(error);   
   })
   .then(function(result) {   
      console.log(result);   
      returngetSum(30, 40); 
      //this returns another Promise   
   }, 
   function(error) {   
      console.log(error);   
   })   
   .then(function(result) {   
      console.log(result);   
   }, 
   function(error) {         
      console.log(error);   
   });   
   console.log("End of script ");   
</script> 

成功执行上述代码后将显示以下输出。

程序首先显示"end of script",然后逐一显示调用 getSum() 方法的结果。

End of script  
11
30
70

这表明 getSum() 是以异步方式或非阻塞方式调用的。Promise 提供了一种简洁明了的方式来处理回调。