Promise API 简介

简介: Promise API 简介 译者注: 到处是回调函数,代码非常臃肿难看, Promise 主要用来解决这种编程方式, 将某些代码封装于内部。

Promise API 简介

译者注: 到处是回调函数,代码非常臃肿难看, Promise 主要用来解决这种编程方式, 将某些代码封装于内部。

Promise 直译为“承诺”,但一般直接称为 Promise;

代码的可读性非常重要,因为开发人员支出一般比计算机硬件的支出要大很多倍。

虽然同步代码更容易跟踪和调试, 但异步方式却具有更好的性能与灵活性.
怎样在同一时刻发起多个请求, 然后分别处理响应结果? Promise 现已成为 JavaScript 中非常重要的一个组成部分, 很多新的API都以 promise 的方式来实现。下面简要介绍 promise, 以及相应的 API 和使用示例!

Promises 周边

XMLHttpRequest 是异步API, 但不算 Promise 方式。当前使用 Promise 的原生 api 包括:

Promise 会越来越流行,所以前端开发需要快速掌握它们。当然, Node.js 是另一个使用 Promise 的平台(显然, Promise 在Node中是一个核心特性)。

测试 promises 可能比你想象的还要容易, 因为 setTimeout 可以用来当作异步“任务”!

Promise 基本用法

直接使用 new Promise() 构造函数的方式, 应该只用来处理遗留的异步任务编程, 例如 setTimeout 或者 XMLHttpRequest。 通过 new 关键字创建一个新的 Promise 对象, 该对象有 resolve(搞定!) 和 reject(拒绝!) 两个回调函数:

var p = new Promise(function(resolve, reject) {
    // ... ... 
    // 此处,可以执行某些异步任务,然后...
    // 在回调中,或者任何地方执行 resolve/reject

    if(/* good condition */) {
        resolve('传入成果结果信息,如 data');
    }
    else {
        reject('失败:原因...!');
    }
});

p.then(function(data) { 
    /* do something with the result */
}).catch(function(err) {
    /* error :( */
});

一般是由开发人员根据异步任务执行的结果,来手动调用 resolve 或者 reject. 一个典型的例子是将 XMLHttpRequest 转换为基于Promise的任务:

// 本段示例代码来源于 Jake Archibald's Promises and Back:
// http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest

function get(url) {
  // 返回一个 promise 对象.
  return new Promise(function(resolve, reject) {
    // 执行常规的 XHR 请求
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
    // Resolve the promise with the response text
    resolve(req.response);
      }
      else {
    // Otherwise reject with the status text
    // which will hopefully be a meaningful error
    reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("网络出错"));
    };

    // Make the request
    req.send();
  });
};

// 使用!
get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.error("Failed!", error);
});

有时候在 promise 方法体中不需要执行异步任务 —— 当然,在有可能会执行异步任务的情况下, 返回 promise 将是最好的方式, 这样只需要给定结果处理函数就行。在这种情况下, 不需要使用 new 关键字, 直接返回 Promise.resolve() 或者 Promise.reject()即可。例如:

var userCache = {};

function getUserDetail(username) {
  // In both cases, cached or not, a promise will be returned

  if (userCache[username]) {
    // Return a promise without the "new" keyword
    return Promise.resolve(userCache[username]);
  }

  // Use the fetch API to get the information
  // fetch returns a promise
  return fetch('users/' + username + '.json')
    .then(function(result) {
      userCache[username] = result;
      return result;
    })
    .catch(function() {
      throw new Error('Could not find user: ' + username);
    });
};

因为总是会返回 promise, 所以只需要通过 thencatch 方法处理结果即可!

then

每个 promise 实例都有 then 方法, 用来处理执行结果。 第一个 then 方法回调的参数, 就是 resolve() 传入的那个值:

new Promise(function(resolve, reject) {
    // 通过 setTimeout 模拟异步任务
    setTimeout(function() { resolve(10); }, 3000);
})
.then(function(result) {
    console.log(result);
});

// console 输出的结果:
// 10

then 回调由 promise 的 resolved 触发。你也可以使用链式的 then` 回调方法:

new Promise(function(resolve, reject) { 
    // 通过 setTimeout 模拟异步任务
    setTimeout(function() { resolve(10); }, 3000);
})
.then(function(num) { console.log('first then: ', num); return num * 2; })
.then(function(num) { console.log('second then: ', num); return num * 2; })
.then(function(num) { console.log('last then: ', num);});

// console 输出的结果:
// first then:  10
// second then:  20
// last then:  40

每个 then 收到的结果都是之前那个 then 返回的值。

如果 promise 已经 resolved, 但之后才调用 then 方法, 则立即触发回调。如果promise被拒绝之后才调用 then, 则回调函数不会被触发。

catch

当 promise 被拒绝时, catch 回调就会被执行:

new Promise(function(resolve, reject) {
    // 通过 setTimeout 模拟异步任务
    setTimeout(function() { reject('Done!'); }, 3000);
})
.then(function(e) { console.log('done', e); })
.catch(function(e) { console.log('catch: ', e); });

// console 输出的结果:
// 'catch: Done!'

传入 reject 方法的参数由你自己决定。一般来说是传入一个 Error 对象:

reject(Error('Data could not be found'));

Promise.all

想想JavaScript加载器的情形: 有时候会触发多个异步交互, 但只在所有请求完成之后才会做出响应。—— 这种情况可以使用 Promise.all 来处理。Promise.all 方法接受一个 promise 数组, 在所有 promises 都搞定之后, 触发一个回调:

Promise.all([promise1, promise2]).then(function(results) {
    // Both promises resolved
})
.catch(function(error) {
    // One or more promises was rejected
});

Promise.all 的最佳示例是通过fetch同时发起多个 AJAX请求时:

var request1 = fetch('/users.json');
var request2 = fetch('/articles.json');

Promise.all([request1, request2]).then(function(results) {
    // Both promises done!
});

你也可以组合使用 fetch 和 Battery 之类的 API ,因为他们都返回 promises:

Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) {
    // Both promises done!
});

当然, 处理拒绝的情况比较复杂。如果某个 promise 被拒绝, 则 catch 将会被第一个拒绝(rejection)所触发:

var req1 = new Promise(function(resolve, reject) { 
    // 通过 setTimeout 模拟异步任务
    setTimeout(function() { resolve('First!'); }, 4000);
});
var req2 = new Promise(function(resolve, reject) { 
    // 通过 setTimeout 模拟异步任务
    setTimeout(function() { reject('Second!'); }, 3000);
});
Promise.all([req1, req2]).then(function(results) {
    console.log('Then: ', one);
}).catch(function(err) {
    console.log('Catch: ', err);
});

// From the console:
// Catch: Second!

随着越来越多的 API 支持 promise, Promise.all 将会变得超级有用。

Promise.race

Promise.race 是一个有趣的函数. 与 Promise.all 相反, 只要某个 priomise 被 resolved 或者 rejected, 就会触发 Promise.race:

var req1 = new Promise(function(resolve, reject) { 
    // 通过 setTimeout 模拟异步任务
    setTimeout(function() { resolve('First!'); }, 8000);
});
var req2 = new Promise(function(resolve, reject) { 
    // 通过 setTimeout 模拟异步任务
    setTimeout(function() { resolve('Second!'); }, 3000);
});
Promise.race([req1, req2]).then(function(one) {
    console.log('Then: ', one);
}).catch(function(one, two) {
    console.log('Catch: ', one);
});

// From the console:
// Then: Second!

一个案例是请求的资源有 主站资源和备用资源(以防某个不可用)。

改变习惯, 使用 Promise

在过去几年中 Promise 一直是个热门话题(如果你是 Dojo Toolkit 用户,那么就是已经有10年了), 已经从一个JavaScript框架变成了语言的一个主要成分. 很快你就会看到大多数新的 JavaScript api 都会基于 Promise 的方式来实现……

… 当然这是一件好事! 开发人员能够避开回调的地狱, 异步交互也可以像其他变量一样传递. Promise 还需要一段时间来普及, 现在是时候去学习他们了!

本文转载自:众成翻译
译者:铁胖子
链接:http://www.zcfy.cc/article/351
原文:https://davidwalsh.name/promises

相关文章
|
6月前
|
前端开发 小程序 API
【微信小程序】-- 使用 npm 包 - API Promise化(四十二)
【微信小程序】-- 使用 npm 包 - API Promise化(四十二)
|
6月前
|
SQL Java 程序员
Java 8中的Stream API:简介与实用案例
【5月更文挑战第23天】本文将深入探讨Java 8中的Stream API,这是一种能够极大提升Java程序员生产力的新特性。我们将从基础概念开始,然后通过一些实用的案例来展示如何使用Stream API进行数据处理和操作。无论你是Java的初学者还是经验丰富的开发者,本文都将为你提供有价值的信息。
|
4月前
|
前端开发 小程序 API
【微信小程序】使用 Promise、async 和 await 将异步API 改写为同步
【微信小程序】使用 Promise、async 和 await 将异步API 改写为同步
61 0
|
6月前
|
安全 Java API
Java 8中的Stream API:简介与实用指南深入理解Java并发编程:线程安全与锁优化
【5月更文挑战第29天】本文旨在介绍Java 8中引入的Stream API,这是一种用于处理集合的新方法。我们将探讨Stream API的基本概念,以及如何使用它来简化集合操作,提高代码的可读性和效率。 【5月更文挑战第29天】 在Java并发编程中,线程安全和性能优化是两个核心议题。本文将深入探讨如何通过不同的锁机制和同步策略来保证多线程环境下的数据一致性,同时避免常见的并发问题如死锁和竞态条件。文章还将介绍现代Java虚拟机(JVM)针对锁的优化技术,包括锁粗化、锁消除以及轻量级锁等概念,并指导开发者如何合理选择和使用这些技术以提升应用的性能。
|
6月前
|
Java API 数据处理
Java 8中的Stream API简介及其在数据处理中的应用
【5月更文挑战第30天】 本文旨在介绍Java 8中引入的Stream API,它是一种用于处理集合的新方法。与传统的循环和迭代器相比,Stream API提供了一种更简洁、高效的方式来处理数据。文章将详细介绍Stream API的基本概念、常用操作以及在实际项目中的应用场景。
|
6月前
|
SQL Java API
Java一分钟之-JPA:Java持久化API简介
【5月更文挑战第14天】Java Persistence API (JPA) 是Java的ORM规范,用于简化数据库操作。常见问题包括实体映射、事务管理和性能问题。避免错误的关键在于明确主键策略、妥善使用事务、优化查询及理解实体生命周期。示例展示了如何定义实体和使用`EntityManager`保存数据。JPA通过标准化API让开发者更专注于业务逻辑,提升开发效率和代码维护性。
67 0
|
前端开发 JavaScript API
Promise API 中的静态方法
捋一捋 Promise 魔法书里的各种静态方法,看看它们有什么用,怎么用,还能给我们带来什么惊喜!🎉
79 1
|
6月前
|
分布式计算 API 数据处理
Flink【基础知识 01】(简介+核心架构+分层API+集群架构+应用场景+特点优势)(一篇即可大概了解flink)
【2月更文挑战第15天】Flink【基础知识 01】(简介+核心架构+分层API+集群架构+应用场景+特点优势)(一篇即可大概了解flink)
162 1
|
6月前
|
JavaScript 前端开发 IDE
Vue3【为什么选择Vue框架、Vue简介 、Vue API 风格 、Vue开发前的准备 、Vue项目目录结构 、模板语法、属性绑定 、 】(一)-全面详解(学习总结---从入门到深化)
Vue3【为什么选择Vue框架、Vue简介 、Vue API 风格 、Vue开发前的准备 、Vue项目目录结构 、模板语法、属性绑定 、 】(一)-全面详解(学习总结---从入门到深化)
97 1
|
小程序 前端开发 API
小程序api封装 promise使用
小程序api封装 promise使用
63 0