【JavaScript】期约与异步函数

在最基本的形式中,JavaScript是一种同步的、阻塞的、单线程的语言,在这种语言中,一次只能执行一个操作。但web浏览器定义了函数和API,允许我们当某些事件发生时不是按照同步方式,而是异步地调用函数(比如,时间的推移,用户通过鼠标的交互,或者获取网络数据)。这意味着您的代码可以同时做几件事情,而不需要停止或阻塞主线程。

JavaScript单线程模型

在最基本的形式中,JavaScript是一种同步的、阻塞的、单线程的编程语言,在这种语言中,一次只能执行一个操作。
JavaScript引擎从文件顶部开始执行脚本,然后一路向下执行。在执行阶段,他创建执行上下文,将函数推入或移出调用栈。
The JavaScript engine executes a script from the top of the file and works its way down. It creates the execution contexts, pushes, and pops functions onto and off the call stack in the execution phase.

事件循环

事件循环(Event Loop)。a constantly running process that coordinates the tasks between the call stack and callback queue to achieve concurrency.
一个持续运行的进程,协调调用栈和回调队列之间的任务,以实现并发。
请添加图片描述

阻塞函数

阻塞函数(blocking function),需要花费很长时间才能执行完成的函数。阻塞函数会阻断网页的所有交互操作。

事件队列

callback queue,task queue。存放异步操作,在主线程完成处理后运行。

JavaScript Event Loop
https://www.javascripttutorial.net/javascript-event-loop/

异步编程实现方法

回调函数和事件;期约。并发和异步(concurrently and asynchronously)

Promise对象

表示一个异步操作最终的状态(待定,完成或失败)及其结果值。
Promise状态转变后不可逆。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise期约三种状态

pending:待定,初始状态。
fulfilled:实现,异步操作成功。
rejected:拒绝,异步操作失败。
当一个期约promise是fulfilled状态时,会调用then()方法。当一个期约promise是rejected状态时,会调用catch()方法。

结果值


the fulfillment value
the rejection reason

无返回、原始值、对象、期约Promise、thenable对象。

构造函数

创建一个新的Promise对象。

// 语法格式
new Promise(executor);

// 参数executor - 一段自定义代码的函数
function(resolutionFunc, rejectionFunc){
      // 通常是一些异步操作
      // do something asynchronous which eventually calls either:
  //
  //   resolve(someValue)        // fulfilled
  // or
  //   reject("failure reason")  // rejected
      }
当异步任务成功时,第一个函数(resolve)将被调用,并返回一个值代表成功。当其失败时,第二个函数(reject)将被调用,并返回失败原因(失败原因通常是一个error对象)。
resolutionFunc(value) // 当被敲定时调用
rejectionFunc(reason) // 当被拒绝时调用

// 返回值
返回一个promise对象。对象的状态由resolutionFunc和rejectionFunc决定,对象的结果值是他们的参数。

静态方法

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

// 语法格式
Promise.resolve(value);
// 参数
value	// 给定的结果值
// 返回值
返回一个带有给定结果值,已实现fulfill状态的Promise对象。

返回值情况:
若参数为值value,则返回一个fulfilled的新期约,期约结果值为value。
若参数为promise,则返回参数promise本身。
若参数为thenable对象,则返回执行thenable对象的then方法后的新期约。

// 等价于
new Promise((resolve, reject) => {resolve(value);});

reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

// 语法格式
Promise.reject(reason);
// 参数
reason	// 被拒绝的原因
// 返回值
返回一个结果值为给定拒绝原因reason,已拒绝rejected状态的Promise对象。

如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例。

实例方法

then方法
为 Promise 实例添加状态改变时的回调函数。指定resolved状态和rejected状态的回调函数。

// 语法格式
p.then([onFulfilled] [, onRejected]);

p.then(value => {
  // fulfillment
}, reason => {
  // rejection
});

// 参数
onFulfilled;  // Promise变成实现状态时调用此处理程序函数,参数value为实现的最终结果(the fulfillment value)
onRejected;  // Promise变成拒绝状态时调用此处理程序函数,参数reason为拒绝的原因(the rejection reason)
// 如果该参数不是函数,则会在内部被替换为“Identity”函数,即(x) => x,原样返回 promise 最终结果的函数
// 如果该参数不是函数,则会在内部被替换为一个 "Thrower" 函数 (it throws an error it received as argument)。
// 如果调用then时一个参数也没有,则then方法返回一个和调用then方法的期约一样状态和结果值的新期约。

// 返回值
返回一个新期约Promise。

then方法返回的新期约遵循以下规则。若then中的回调函数:
返回了一个值,那么 then 返回的 Promise 状态已锁定resolved,并且结果值为 **返回的值**。
没有返回任何值,那么 then 返回的 Promise 状态已锁定resolved,并且结果值为 **undefined**。
抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为结果值。
返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为实现 fulfilled 状态,并且结果值为返回语句中 Promise 的实现值(fulfillment value)。
返回一个已经是拒绝状态的 Promise,那么 then 返回的 Promise 也会成为拒绝 rejected 状态,并且结果值为返回语句中 Promise 的拒绝原因(rejection reason)。
返回一个未定状态(pending)的 Promise,那么 then 返回的 Promise 状态也是未定的。

then方法的第一个参数是resolved状态的回调函数(callback function),第二个参数是rejected状态的回调函数,它们都是可选的。返回一个新的Promise实例。

catch方法
为Promise添加拒绝状态时的回调函数。返回一个新的Promise。p.catch(onRejected);相当于调用p.then(undefined,onRejected);

// 语法格式
p.catch(onRejected);
p.catch(function(reason){
	// rejection}
);

// 参数
onRejected  // 函数,当Promise状态为rejected失败时调用
reason  // 失败的原因。

// 返回值
返回一个新Promise。
// 调用catch时无参数,则返回一个新期约。新期约的状态和结果值和调用then方法的期约一致。

catch方法返回的新期约遵循以下规则。若catch中的回调函数:
**返回rejected状态的Promise**,则catch方法返回一个rejected状态的新期约,并且新期约结果值为回调函数返回语句中期约的拒绝原因。
**抛出一个错误**,则catch方法返回一个rejected的新期约,并且新期约结果值为抛出的错误。
**其余情况**都返回一个fulfilled状态的新期约。

finally方法
回调函数,无论Promise是否成功完成后都需要执行的代码。

// 语法格式
p.finally(onFinally);

p.finally(function() {
   // settled (fulfilled or rejected)
});

// 参数
onFinally	// Promise为已确定settled状态被调用的回调函数

// 返回值
返回一个新的Promise。Promise的状态和结果值和调用finally的Promise相同。

then(),catch(),finally() 都定义在原型对象Promise.prototype上。

Promise术语回顾

创建promise时,它既不是成功也不是失败状态。这个状态叫作pending(待定)。
当promise返回时,称为 resolved(已解决).
一个成功resolved的promise称为fullfilled(实现)。它返回一个值,可以通过将.then()块链接到promise链的末尾来访问该值。 .then()块中的执行程序函数将包含promise的返回值。
一个不成功resolved的promise被称为rejected(拒绝)了。它返回一个原因(reason),一条错误消息,说明为什么拒绝promise。可以通过将.catch()块链接到promise链的末尾来访问此原因。

Promises 对比 callbacks

promises与旧式callbacks有一些相似之处。它们本质上是一个返回的对象,您可以将回调函数附加到该对象上,而不必将回调作为参数传递给另一个函数。

然而,Promise是专门为异步操作而设计的,与旧式回调相比具有许多优点:

您可以使用多个then()操作将多个异步操作链接在一起,并将其中一个操作的结果作为输入传递给下一个操作。这种链接方式对回调来说要难得多,会使回调以混乱的“末日金字塔”告终。 (也称为回调地狱)。
Promise总是严格按照它们放置在事件队列中的顺序调用。
错误处理要好得多——所有的错误都由块末尾的一个.catch()块处理,而不是在“金字塔”的每一层单独处理。

chooseToppings(function(toppings) {
  placeOrder(toppings, function(order) {
    collectOrder(order, function(pizza) {
      eatPizza(pizza);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);



chooseToppings()
.then(function(toppings) {
  return placeOrder(toppings);
})
.then(function(order) {
  return collectOrder(order);
})
.then(function(pizza) {
  eatPizza(pizza);
})
.catch(failureCallback);

Promise VS thenable

thenable - Any object that has a method named “then” is called a “thenable” object.

const obj = {
  then() {
    // some codes are here
  }
};
// obj is a thenable object
// Resolve一个thenable对象
var p1 = Promise.resolve({
  then: function(onFulfill, onReject) { onFulfill("fulfilled!"); }
});
console.log(p1 instanceof Promise) // true, 这是一个Promise对象

p1.then(function(v) {
    console.log(v); // 输出"fulfilled!"
  }, function(e) {
    // 不会被调用
});

// Thenable在callback之前抛出异常
// Promise rejects
var thenable = { then: function(resolve) {
  throw new TypeError("Throwing");
  resolve("Resolving");
}};

var p2 = Promise.resolve(thenable);
p2.then(function(v) {
  // 不会被调用
}, function(e) {
  console.log(e); // TypeError: Throwing
});

// Thenable在callback之后抛出异常
// Promise resolves
var thenable = { then: function(resolve) {
  resolve("Resolving");
  throw new TypeError("Throwing");
}};

var p3 = Promise.resolve(thenable);
p3.then(function(v) {
  console.log(v); // 输出"Resolving"
}, function(e) {
  // 不会被调用
});

every promise object is just a thenable object, but every thenable object is not a promise object.

异步函数(ES8)

ES6期约模式在ECMAScript函数中的应用。
async/await。使得同步方式书写的代码能够异步执行。解决利用异步结构组织代码的问题。
async 关键字用于声明异步函数。异步函数始终返回期约对象。异步函数如果使用 return 关键字返回了值(如果没有 return 则会返回 undefined),这个值会被 Promise.resolve()包装成一个期约对象。异步函数始终返回期约对象。
await 关键字可以暂停异步函数代码的执行,等待期约解决。await 关键字会暂停执行异步函数后面的代码,让出 JavaScript 运行时的执行线程。await 关键字尝试“解包”对象的值,然后将这个值传给表达式,再异步恢复异步函数的执行。
实际async是一个标识符,标识函数是异步函数。await真正进行异步操作,控制停止和恢复执行。

async function foo() { // 异步函数
 console.log(1); 
 
 throw 3; // 抛出错误,返回拒绝原因为3的拒绝期约 
 Promise.reject(3); // 单独的拒绝期约不会被异步函数捕获

 await (() => { throw 3; })(); // 抛出错误。返回期约拒绝原因为3的拒绝期约
 await Promise.reject(3); // 对拒绝期约使用await会释放(unwrap)错误值,即将拒绝期约返回
 // 除抛出错误和拒绝期约,其他情况都需要return才能返回期约
 
 return 原始值/对象/期约/thenable对象;  // 返回相应的期约
 // 若异步函数没有返回语句,则自动返回状态为fulfilled、结果值为undefined的期约
 
 console.log(4); // 这行代码不会执行
} 
// 给返回的期约添加一个拒绝处理程序
foo().catch(console.log); 
console.log(2);

// 1
// 2
// 3

Anything you await is passed through Promise.resolve() , so you can safely await non-native promises.

总结

添加期约和异步函数的 目的:掌握单线程 JavaScript 运行时的异步行为

期约 主要功能是为异步代码提供了清晰的抽象。

  • 可以用期约表示异步执行的代码块,也可以用期约表示异步计算的值。在需要串行异步代码时,期约的价值最为突出。作为可塑性极强的一种结构,期约可以被序列化、连锁使用、复合、扩展和重组。

异步函数 将期约应用于 JavaScript 函数的结果。

  • 异步函数可以暂停执行,而不阻塞主线程。无论是编写基于期约的代码,还是组织串行或平行执行的异步代码,使用异步函数都非常得心应手。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值