js学习笔记4

93 阅读4分钟
1.同步与异步

同步
同步行为对应内存中顺序执行的处理器指令。每条指令都会严格按照它们出现的顺序来执行,而每 条指令执行后也能立即获得存储在系统本地(如寄存器或系统内存)的信息。后面的指令总是在前面的指令完成后才 会执行。等到最后一条指定执行完毕

异步
相对地,异步行为类似于系统中断,即当前进程外部的实体可以触发代码执行。异步操作经常是必 要的,因为强制进程等待一个长时间的操作通常是不可行的(同步操作则必须要等)。如果代码要访问 一些高延迟的资源,比如向远程服务器发送请求并等待响应,那么就会出现长时间的等待。

function double(value) {
setTimeout(() => setTimeout(console.log, 0, value * 2), 1000);
}
double(3);

为了让后续代码能够使用x,异步执行的函数需要在更新x 的值以后通知其他代码。如果程序不需要这个值,那么就只管继续执行,不必等待这个结果了。

关键
理解它是一个异步函数。setTimeout 可以定义一个 在指定时间之后会被调度执行的回调函数。对这个例子而言,1000 毫秒之后,JavaScript 运行时会把回 调函数推到自己的消息队列上去等待执行。推到队列之后,回调什么时候出列被执行对JavaScript 代码 就完全不可见了。还有一点,double()函数在setTimeout 成功调度异步操作之后会立即退出。
1.1 异步返回值
设setTimeout 操作会返回一个有用的值。有什么好办法把这个值传给需要它的地方?广泛接受 的一个策略是给异步操作提供一个回调,这个回调中包含要使用异步返回值的代码(作为回调的参数)。

function double(value, callback) {
setTimeout(() => callback(value * 2), 1000);
}
double(3, (x) => console.log(`I was given: ${x}`));
// I was given: 6(大约1000 毫秒之后)

这里的setTimeout 调用告诉JavaScript 运行时在1000 毫秒之后把一个函数推到消息队列上。这 个函数会由运行时负责异步调度执行。而位于函数闭包中的回调及其参数在异步执行时仍然是可用的。
1.2失败处理
成功回调和失败回调:

function double(value, success, failure) {
setTimeout(() => {
try {
if (typeof value !== 'number') {
throw 'Must provide number as first argument';
}
success(2 * value);
} catch (e) {
failure(e);
}
}, 1000);
}
const successCallback = (x) => console.log(`Success: ${x}`);
const failureCallback = (e) => console.log(`Failure: ${e}`);
double(3, successCallback, failureCallback);
double('b', successCallback, failureCallback);
// Success: 6(大约1000 毫秒之后)
// Failure: Must provide number as first argument(大约1000 毫秒之后)

这种模式已经不可取了,因为必须在初始化异步操作时定义回调。异步函数的返回值只在短时间内 存在,只有预备好将这个短时间内存在的值作为参数的回调才能接收到它。

1.3嵌套异步回调
回调地狱如果异步返值又依赖另一个异步返回值,那么回调的情况还会进一步变复杂,不可取

2.promise

引用类型Promise,可以通过new 操作符来实例化
并有以下三种状态
pending,fulfilled,rejected
pending是promise的最初始状态。在待定状态下,promise可以settle为代表成功的 fulfilled状态,或者代表失败的reject状态。无论落定为哪种状态都是不可逆的。

let p = new Promise((resolve, reject) => {
setTimeout(reject, 10000); // 10 秒后调用reject()
// 执行函数的逻辑
});
setTimeout(console.log, 0, p); // Promise <pending>
setTimeout(console.log, 11000, p); // 11 秒后再检查状态
// (After 10 seconds) Uncaught error
// (After 11 seconds) Promise <rejected> 

Promise.resolve()

let p1 = new Promise((resolve, reject) => resolve());
let p2 = Promise.resolve();

依然会返回一个promise

Promise.reject()
与Promise.resolve()类似,Promise.reject()会实例化一个拒绝的期约并抛出一个异步错误

3.实例方法

Promise.prototype.then()
这个then()方法接收最多两个参数:onResolved 处理程序和onRejected 处理程序,返新的promise实例

function onResolved(id) {
setTimeout(console.log, 0, id, 'resolved');
}
function onRejected(id) {
setTimeout(console.log, 0, id, 'rejected');
}
let p1 = new Promise((resolve, reject) => setTimeout(resolve, 3000));
let p2 = new Promise((resolve, reject) => setTimeout(reject, 3000));
p1.then(() => onResolved('p1'),
() => onRejected('p1'));
p2.then(() => onResolved('p2'),
() => onRejected('p2'));
//(3 秒后)
// p1 resolved
// p2 rejected
let p1 = new Promise(() => {});
let p2 = p1.then();
setTimeout(console.log, 0, p1); // Promise <pending>
setTimeout(console.log, 0, p2); // Promise <pending>
setTimeout(console.log, 0, p1 === p2); // false

Promise.prototype.catch()
Promise.prototype.catch()方法用于给期约添加拒绝处理程序。这个方法只接收一个参数: onRejected 处理程序,返新的promise实例

let p = Promise.reject();
let onRejected = function(e) {
setTimeout(console.log, 0, 'rejected');
};
// 这两种添加拒绝处理程序的方式是一样的:
p.then(null, onRejected); // rejected
p.catch(onRejected); // rejected

Promise.prototype.finally()
Promise.prototype.finally()方法用于给Promise添加onFinally 处理程序,这个处理程序在Promise转换为解决或拒绝状态时都会执行。

let p1 = Promise.resolve();
let p2 = Promise.reject();
let onFinally = function() {
setTimeout(console.log, 0, 'Finally!')
}
p1.finally(onFinally); // Finally
p2.finally(onFinally); // Finally
Promise.prototype.finally()方法返回一个新的期约实例:
let p1 = new Promise(() => {});
let p2 = p1.finally();
setTimeout(console.log, 0, p1); // Promise <pending>
setTimeout(console.log, 0, p2); // Promise <pending>
setTimeout(console.log, 0, p1 === p2); // false

这个新Promise实例不同于then()或catch()方式返回的Promise实例。因为onFinally 被设计为一个状态 无关的方法,所以在大多数情况下它将表现为父Promise的传递

let p1 = Promise.resolve('foo');
// 这里都会原样后传
let p2 = p1.finally();
let p3 = p1.finally(() => undefined);
let p4 = p1.finally(() => {});
let p5 = p1.finally(() => Promise.resolve());
let p6 = p1.finally(() => 'bar');
let p7 = p1.finally(() => Promise.resolve('bar'));
let p8 = p1.finally(() => Error('qux'));
setTimeout(console.log, 0, p2); // Promise <resolved>: foo
setTimeout(console.log, 0, p3); // Promise <resolved>: foo
setTimeout(console.log, 0, p4); // Promise <resolved>: foo
setTimeout(console.log, 0, p5); // Promise <resolved>: foo
setTimeout(console.log, 0, p6); // Promise <resolved>: foo
setTimeout(console.log, 0, p7); // Promise <resolved>: foo
setTimeout(console.log, 0, p8); // Promise <resolved>: foo