【译】怎么写一个JavaScript Promise

简介: JavaScript promise是一个对象,表示异步任务完成或者失败及其结果值。

promise是什么?


JavaScript promise是一个对象,表示异步任务完成或者失败及其结果值。

完结。


我当然是开玩笑的。那么,这个定义到底意味着什么?


首先,JavaScript中的许多东西都是对象。你可以通过几种不同的方式进行创建对象。最常用的方法是使用对象字面量语法


const myCar = {
   color: 'blue',
   type: 'sedan',
   doors: '4',
};


你还可以创建一个,并通过new关键字对其进行实例化。


class Car {
   constructor(color, type, doors) {
      this.color = color;
      this.type = type;
      this.doors = doors
   }
}
const myCar = new Car('blue', 'sedan', '4');


console.log(myCar);


image.png


promise只是我们创建的对象,就像后面的例子一样,我们使用new关键字对其进行实例化。我们传入一个带有两个参数的函数,其参数为resolvereject,而不是像传递给我们Car的三个参数(颜色,类型和门)。


最终,promise告诉我们一些关于我们从它返回的异步函数的完成情况--生效了或失败了。我们认为这个功能是成功的,如果promise是解决了,并且说promise被拒绝是不成功的。


const myPromise = new Promise(function(resolve, reject) {});


console.log(myPromise);


image.png


留意,此时的promise是pending状态


const myPromise = new Promise(function(resolve, reject) {
   resolve(10);
});


image.png


留意,我们用10返回解决了promise


看,不是太可怕 -- 只是我们创建的对象。而且,如果我们稍微展开一下:


image.png


留意,我们有一些我们可以访问的方法,即"then"和"catch"


此外,我们可以传我们喜欢的东西到resolvereject中。例如,我们可以传递一个对象,而不是一个字符串:


return new Promise((resolve, reject) => {
   if(somethingSuccesfulHappened) {
      const successObject = {
         msg: 'Success',
         data,//...some data we got back
      }
      resolve(successObject); 
   } else {
      const errorObject = {
         msg: 'An error occured',
         error, //...some error we got back
      }
      reject(errorObject);
   }
});


或者,为了方便查看,我们任何东西都不传:


return new Promise((resolve, reject) => {
   if(somethingSuccesfulHappend) {
      resolve()
   } else {
      reject();
   }
});


定义“异步”的部分怎样?


JavaScript是单线程的。这意味着它一次只能处理一件事。想象这么条道路,你可以将JavaScript视为单车道的高速公路。特定代码(异步代码)可以滑动到一边,以允许其他代码越过它。完成异步代码后,它将返回到道路。


旁注,我们可以从任何函数返回promise。他不必是异步的。话虽这么说,promise通常在它们返回的函数是异步的情况下返回。例如,具有将数据保存在服务器的方法API将是返回promise的绝佳候选者!


外号:


promise为我们提供了一种等待异步代码完成,从中捕获一些值,并将这些值传递给程序其他部分的方法。


我这里有篇文章深入探讨这些概念:Thrown For a Loop: Understanding Loops and Timeouts in JavaScript


我们怎么使用promise?


使用promise也称为消费promise。在上面的示例中,我们的函数返回了一个promise对象。这允许我们使用方法的链式功能。


我打赌你看到过下面的这种链式方法:


const a = 'Some awesome string';
const b = a.toUpperCase().replace('ST', '').toLowerCase();
console.log(b); // some awesome ring


现在,(假装)回想下我们的promise


const somethingWasSuccesful = true;
function someAsynFunction() {
   return new Promise((resolve, reject){
      if (somethingWasSuccesful) {
         resolve();     
      } else {
         reject()
      }
   });
}


然后,通过链式方法调用我们的promise


someAsyncFunction
   .then(runAFunctionIfItResolved(withTheResolvedValue))
   .catch(orARunAfunctionIfItRejected(withTheRejectedValue));


一个(更)真实的例子


想象一下,你有一个从数据库中获取用户的功能。我在codepen上编写了一个示例函数,用于模拟你可能使用的API。它提供了两种访问结果的选项。一,你可以提供回调功能,在其中访问用户或提示错误。或者第二种,函数返回一个promise作为用户访问或提示错误的方法。


为了方便查看,我把作者的codepen上的代码复制了下来,如下:


const users = [
  {
    id: '123',
    name: 'John Smith',
    posts: [
      {title: 'Some amazing title', content: 'Here is some amazing content'},
      {title: 'My favorite title', content: 'My favorite content'},
      {title: 'A not-so-good title', content: 'The not-so-good content'},
    ]
  },
  {
    id: '456',
    name: 'Mary Michaels',
    posts: [
      {title: 'Some amazing title', content: 'Here is some amazing content'},
      {title: 'My favorite title', content: 'My favorite content'},
      {title: 'A not-so-good title', content: 'The not-so-good content'},
    ]
  },
]
function getUserPosts(id, cb) {
 const user = users.find(el => el.id === id);
  if (cb) {
    if (user) {
      return cb(null, user);
    }
    return cb('User Not Found', null);
  }
  return new Promise(function(resolve, reject){
    if (user) {
      resolve(user);
    } else {
      reject('User not found');
    }
  });
}
/* The above code is collapsed to simulate an API you might use to get user posts for a
 * particular user from a database.
 * The API can take a callback as a second argument: getUserPosts(<id>, <callback>); 
 * The callback function first argument is any error and second argument is the user.
 * For example: 
    getUserPosts('123', function(err, user) {
      if (err) { 
        console.log(err) 
      } else {
        console.log(user);
      }
    });
 * getUserPosts also returns a promise, for example: getUserPosts.then().catch();
 * The ID's that will generate a user are the of type string and they are '123' and '456'.
 * All other IDs will return an error.
 */
getUserPosts('123', function(err, user) {
  if (err) {
    console.log(err);
  } else {
    console.log(user);
  }
});
getUserPosts('129', function(err, user) {
  if (err) {
    console.log(err);
  } else {
    console.log(user);
  }
});
getUserPosts('456')
  .then(user => console.log(user))
  .catch(err => console.log(err));


传统上,我们将通过使用回调来访问异步代码的结果。


rr someDatabaseThing(maybeAnID, function(err, result)) {
   //...Once we get back the thing from the database...
   if(err) {
      doSomethingWithTheError(error)
   }   else {
      doSomethingWithResults(results);
   }
}


在它们变得过度嵌套之前,回调的使用是可以的。换句话说,你必须为每个新结果运行更多异步代码。回调的这种模式可能会导致“回调地狱”。


image.png


Promise为我们提供了一种更优雅,更易读的方式来查看我们程序流程。


doSomething()
   .then(doSomethingElse) // and if you wouldn't mind
   .catch(anyErrorsPlease);


写下自己的promise:金发姑娘,三只熊和一台超级计算机


想象一下,你找到了一碗汤。在你喝之前,你想知道汤的温度。但是你没有温度计,幸运的是,你可以使用超级计算机来告诉你汤的温度。不幸的是,这台超级计算机最多可能需要10秒才能获得结果。


image.png


这里需要有几点需要注意:


  1. 我们初始化了一个名为result的全局变量。
  2. 我们使用Math.random()setTimeout()模拟网络延迟的持续时间。
  3. 我们使用Manth.random()模拟温度。
  4. 我们通过添加一些额外的“math“将延迟和温度限制在一定范围内。温度范围是1到300;延迟范围是1000ms到10000ms(1s到10s)。
  5. 我们打印出延迟时间和温度,以便我们知道这个功能需多长时间以及我们期望在完成时看到的结果。


运行函数并打印结果。


getTemperature(); 
console.log(results); // undefined


温度是undefined,发生了什么?


该功能需要一定的时间才能运行。在延迟结束之前,不会设置变量。因此,当我们运行该函数时,setTimeout是异步的。setTimeout中的部分代码移出主线程进入等待区域。


我这里有篇文章深入研究了这个过程:Thrown For a Loop: Understanding Loops and Timeouts in JavaScript


由于设置变量result的函数部分移动到了等待区域直到完成,因此我们的解析器可以自由移动到下一行。在我们的例子中,它是我们的console.log()。此时,由于我们的setTimeout未结束,result仍未定义。


那我们还能尝试什么呢?我们可以运行getTemperature(),然后等待11秒(因为我们的最大延迟是10秒),然后打印出结果。


getTemperature();
   setTimeout(() => {
      console.log(result); 
   }, 11000);
// Too Hot | Delay: 3323 | Temperature: 209 deg


这是可行的,但这种技术问题是,尽管在我们的例子中,我们知道了最大的网络延迟,但在实际中它可能偶尔需要超过10秒。而且,即使我们可以保证最大延迟10秒,如果result出结果了,我们也是在浪费时间。


promise来拯救


我们将重构getTemperature()函数以返回promise。而不是设置结果。我们将拒绝promise,除非结果是“恰到好处”,在这种情况下我们将解决promise。在任何一种情况下,我们都会传递一些值到resolvereject


image.png


现在,我们可以使用正在返回的promise结果(也称为消费promise)。


getTemperature()
   .then(result => console.log(result))
   .catch(error => console.log(error));
// Reject: Too Cold | Delay: 7880 | Temperature: 43 deg


.then,当我们的promise解决时,它将被调用,并返回我们传递给resolve的任何信息。


.catch,当我们的promise拒绝时,它将被调用,并返回我们传递给reject的任何信息。


最有可能的是,你将更多的使用promise,而不是创建它们。在任何情况下,它们有助于使我们的代码更优雅,可读和高效。


总结


  1. Promises是对象,其包含了有关某些异步代码的完成以及我们想要传入的任何结果值的信息对象。
  2. 我们使用return new Promise((resolve, reject)=> {})返回一个promise。
  3. 使用promise,我们使用.then从已经解决的promise中获取信息,然后使用.catch从拒绝的promise中获取信息。
  4. 你可能更多地使用(消费)promises,而不是编写它们。


相关文章
|
2月前
|
前端开发 JavaScript
如何处理 JavaScript 中的异步操作和 Promise?
如何处理 JavaScript 中的异步操作和 Promise?
15 1
|
2月前
|
前端开发 JavaScript
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
25 4
|
2月前
|
前端开发 JavaScript 开发者
JavaScript 中的异步编程:Promise 和 Async/Await
在现代的 JavaScript 开发中,异步编程是至关重要的。本文将介绍 JavaScript 中的异步编程概念,重点讨论 Promise 和 Async/Await 这两种常见的处理异步操作的方法。通过本文的阐述,读者将能够更好地理解和应用这些技术,提高自己在 JavaScript 开发中处理异步任务的能力。
|
7天前
|
JSON 前端开发 JavaScript
【JavaScript技术专栏】JavaScript异步编程:Promise、async/await解析
【4月更文挑战第30天】JavaScript中的异步编程通过Promise和async/await来解决回调地狱问题。Promise代表可能完成或拒绝的异步操作,有pending、fulfilled和rejected三种状态。它支持链式调用和Promise.all()、Promise.race()等方法。async/await是ES8引入的语法糖,允许异步代码以同步风格编写,提高可读性和可维护性。两者结合使用能更高效地处理非阻塞操作。
|
13天前
|
前端开发 JavaScript
在JavaScript中,回调函数、Promise和async/await这三种异步处理机制的优缺点
JavaScript的异步处理包括回调函数、Promise和async/await。回调函数简单易懂,但可能导致回调地狱和错误处理困难。Promise通过链式调用改善了这一情况,但仍有回调函数需求和学习成本。async/await提供同步风格代码,增强可读性和错误处理,但需ES8支持,不适用于并发执行。根据项目需求选择合适机制。
|
2月前
|
前端开发 JavaScript
js开发:请解释Promise是什么,以及它如何解决回调地狱(callback hell)问题。
Promise是JavaScript解决异步操作回调地狱的工具,代表未来可能完成的值。传统的回调函数嵌套导致代码难以维护,而Promise通过链式调用`.then()`和`.catch()`使异步流程清晰扁平。每个异步操作封装为Promise,成功时`.then()`传递结果,出错时`.catch()`捕获异常。ES6的`async/await`进一步简化Promise的使用,使异步代码更接近同步风格。
19 1
|
3月前
|
前端开发 JavaScript API
JavaScript学习笔记(一)promise与async
JavaScript学习笔记(一)promise与async
|
3月前
|
前端开发 JavaScript UED
JavaScript中的异步编程和Promise
【2月更文挑战第3天】在Web开发中,JavaScript是一门非常重要的编程语言,而异步编程是JavaScript中的一个关键概念。本文将介绍JavaScript中的异步编程特点,以及如何使用Promise来更加优雅地处理异步操作,帮助开发者更好地理解和应用这一技术。
18 3
|
3月前
|
前端开发 JavaScript 数据处理
JavaScript中的异步编程及Promise对象
【2月更文挑战第3天】 传统的JavaScript编程模式在处理异步任务时常常会导致回调地狱和代码可读性较差的问题,而Promise对象的引入为解决这一问题提供了一种优雅的解决方案。本文将介绍JavaScript中的异步编程方式以及Promise对象的使用方法和优势,帮助读者更好地理解和运用异步编程技术。
21 8
|
3月前
|
前端开发 JavaScript
JavaScript中的异步编程与Promise
【2月更文挑战第1天】在Web开发中,JavaScript是一种常用的编程语言,而异步编程是其重要特点之一。本文将介绍JavaScript中的异步编程机制,探讨Promise对象的使用以及如何通过Promise处理异步操作,帮助读者更好地理解和应用JavaScript异步编程技术。