ES6 之异步流程的前世今生(上)
本文讲述了异步流程的演变过程。那么是什么是异步编程呢: 简单来讲就是执行一个指令不会马上返回结果而执行下一个任务,而是等到特定的事件触发后,才能得到结果。
基础知识
我们知道 javascript 运行在浏览器中,以 Google 浏览器为例子, v8 引擎,包含 内存堆: 这是内存分配发生的地方。 调用栈: 这是你代码执行的地方。
运行一个函数时,解析器把该函数添加到栈中并且执行这个函数。
Web APIs: DOM、AJAX、Timeout(setTimeout)
js是一门单线程的语言, 这意味这它只有一个调用栈。
当我们堆栈执行的函数需要大量时间时,浏览器会停止响应,幸运的是我们有异步回调。
javaScript引擎 运行在宿主环境中(浏览器或者 node),
CallbackQueue and Event Loop
事件循环和回调队列
调用栈和回调队列,当栈为空时,它会调取出队列中的第一个事件,放到调用栈中执行;
常见的 宏任务(macro-task)(这个队列也被叫做 task queue) 比如: setTimeout、setInterval、 setImmediate、script(整体代码)、 I/O 操作、UI 渲染等。
常见的 微任务(micro-task) 比如: process.nextTick、Promise、Object.observe、MutationObserver 等。
常见的异步编程方案
回调函数
事件监听
发布/订阅
promise 对象
环境配置
一双能敲代码的手、一台能执行代码的电脑。 需要预先引入的库
const fs = require('fs')
const co = require('co')
const util = require('util')
callback()
第一阶段:回调函数
function readFile(cb) {
fs.readFile("./package.json", (err, data) => {
if (err) return cb(err);
cb(null, data);
});
}
readFile((err, data) => {
if (!err) {
data = JSON.parse(data);
console.log(data.name);
}
});
回调函数的弊端
代码书写顺序与执行顺序不一致,不利于维护
回调函数大多是匿名函数,bug 追踪困难
异步操作的代码变更,后期维护麻烦。
事件监听
采用了事件驱动模型,任务的执行不取决与代码的顺序,取决于某个事件是否发生。
function f1() {
setTimeout(function () {
// f1的任务代码
f1.trigger("done");
}, 1000);
}
发布/订阅
假定我们存在一个任务中心,当某个事件完成之后,我们就发射状态信号,调度中心可以通知订阅了该状态信号的其他任务。这个也称为观察者模式。
jQuery.subscribe("done", f2);
function f1(){
setTimeout(function () {
// f1的任务代码
jQuery.publish("done");
}, 1000);
}
当f1 执行完成后, 向信号中心"jquery"发布"done"信号,从而引发f2的执行。
promise
第二阶段:Promise
定义阶段:promise(resolve, reject)分别成功或者失败时处理什么。
调用阶段:通过 then 函数实现,成功就执行 resolve,它会将 reslove 的值传递给最近的 then 函数,作为 then 函数的参数。如果出错 reject,那么交给 catch 来捕获异常
promise 的要点如下:
递归: 每个一步操作返回的都是 promise 对象
状态机: 三种状态 peomise 对象内部可以控制,不能在外部改变状态
全局异常处理
将回调函数中的结果延后到 then 函数里处理或交给全局异常处理
我们约定将每个函数的返回值都得是 promise 对象。 只要是 promise 对象, 就可以控制状态并支持 then 方法,将无限个 promise 对象链接在一起。
hello("xx.html")
.then(log)
.then(function () {
return world("./xxx.js").then(log);
})
.catch((err) => {
console.log(err);
});
每个 promise 对象都有 then 方法, 也就是说 then 方法是定义在原型对象 promise.prototype 上的, 它的作用是为 promise 实例添加状态改变时的回调函数
Promise.prototype.then() = function (success, fail) {
this.done(success)
this.fail(fail)
return this
}
一般情况下,只传 success 回调函数即可,fail 函数可选,使用 catch 来捕获函数异常比通过 fail 函数进行处理更加可控。
const requireDirectory = require(require - directory);
module.export = requireDirectory(module);
function readFileAsync(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
readFileAsync("./package.json")
.then((data) => {
data = JSON.parse(data);
console.log(data.name);
})
.catch((err) => {
console.log(err);
});
我们来看看下面这个例子:
new Promise(function (resolve) {
resolve(1);
}).then(function (value) {
console.log(value);
});
Promise.resolve(1).then(function (value) {
console.log(" Promise.resovle" + value);
});
var error = new Error("this is a error");
new Promise 更为强大,Promise.resolve 更为便捷
以下是更为便捷的写法
function hello(i) {
return Promise.resolve(i);
}
hello(1).then(function () {
console.log("promise.reslove1=" + value);
});
// Promise.resolve返回的是prmise对象,相当于 new Promise(resolve,reject)实例
// Promise.prototype.then()方法的语法如下
p.then(onFulfilled, onRejected);
// p.then(function(value)){}
p.catch(onRejected);
// p.catch(function(reson)) {}
//整个promise 还有这种写法
hell("./xx.json")
.then(function (data) {
return new Promise(function (reslove, reject) {
console.log("promise " + data);
reslove(data);
});
})
.then(function (data) {
return new Promise(function (reslove, reject) {
console.log("promise " + data);
reslove(data);
});
})
.then(function (data) {
return new Promise(function (reslove, reject) {
console.log("promise " + data);
reslove(data);
});
})
.catch(function (err) {
console.log(err);
});
promise 原理
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function MyPromise(executor) {
// executor:这是实例Promise对象时在构造器中传入的参数,一般是一个function(resolve,reject){}
let that = this;
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
// 要确保 onFulfilled 和 onRejected 方法异步执行
setTimeout(() => {
// 调用resolve 回调对应onFulfilled函数
if (that.status === PENDING) {
// 只能由pedning状态 => fulfilled状态 (避免调用多次resolve reject)
that.status = FULFILLED;
that.value = value;
that.onFulfilledCallbacks.forEach((cb) => cb(that.value));
}
});
}
function reject(reason) {
setTimeout(() => {
// 调用reject 回调对应onRejected函数
if (that.status === PENDING) {
// 只能由pedning状态 => rejected状态 (避免调用多次resolve reject)
that.status = REJECTED;
that.reason = reason;
that.onRejectedCallbacks.forEach((cb) => cb(that.reason));
}
});
}
// executor方法可能会抛出异常,需要捕获
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
MyPromise.prototype.then = function (onFulfilled, onRejected) {
//获取下this
let self = this;
if (this.status === FULFILLED) {
onFulfilled(self.value);
}
if (this.status === REJECTED) {
onRejected(self.value);
}
//异步时处理
if (this.status === PENDING) {
//保存回调函数
this.onFulfilledCallbacks.push(() => {
onFulfilled(self.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(self.reason);
});
}
};
var mp = new MyPromise((resolve, reject) => {
console.log(11111);
setTimeout(() => {
resolve(22222);
console.log(3333);
}, 1000);
});
mp.then(
(x) => {
console.log(x);
console.log("4444");
},
(err) => {
console.log("err2", err);
}
);
上述代码就是一个简单的 promise 了,但是还有两点问题没有解决 1.链式调用 2.不传值时。 我们改造下
MyPromise.prototype.then = function(onFulfilled, onRejected) {
//获取下this
let that = this;
//链式调用 在创建一个promise
let promsie2 = null;
//解决onFulfilled、onRejected没有传值的问题
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y
onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}
if (that.status === FULFILLED) { // 成功态
return newPromise = new MyPromise((resolve, reject) => {
setTimeout(() => {
try{
let x = onFulfilled(that.value);
resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
} catch(e) {
reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
}
});
})
}
if (that.status === REJECTED) { // 失败态
return newPromise = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(that.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
if (that.status === PENDING) { // 等待态
// 将onFulfilled/onRejected收集暂存到集合中
return newPromise = new MyPromise((resolve, reject) => {
that.onFulfilledCallbacks.push((value) => {
try {
let x = onFulfilled(value);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
that.onRejectedCallbacks.push((reason) => {
try {
let x = onRejected(reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
})
};
resolvePromsie 是什么呢: Promise A+ 2.27 规范
/**
* resolve中的值几种情况:
* 1.普通值
* 2.promise对象
* 3.thenable对象/函数
*/
/**
* 对resolve 进行改造增强 针对resolve中不同值情况 进行处理
* @param {promise} promise2 promise1.then方法返回的新的promise对象
* @param {[type]} x promise1中onFulfilled的返回值
* @param {[type]} resolve promise2的resolve方法
* @param {[type]} reject promise2的reject方法
*/
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
// 如果从onFulfilled中返回的x 就是promise2 就会导致循环引用报错
return reject(new TypeError("循环引用"));
}
let called = false; // 避免多次调用
// 如果x是一个promise对象 (该判断和下面 判断是不是thenable对象重复 所以可有可无)
if (x instanceof Promise) {
// 获得它的终值 继续resolve
if (x.status === PENDING) {
// 如果为等待态需等待直至 x 被执行或拒绝 并解析y值
x.then(
(y) => {
resolvePromise(promise2, y, resolve, reject);
},
(reason) => {
reject(reason);
}
);
} else {
// 如果 x 已经处于执行态/拒绝态(值已经被解析为普通值),用相同的值执行传递下去 promise
x.then(resolve, reject);
}
// 如果 x 为对象或者函数
} else if (x != null && (typeof x === "object" || typeof x === "function")) {
try {
// 是否是thenable对象(具有then方法的对象/函数)
let then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(reason) => {
if (called) return;
called = true;
reject(reason);
}
);
} else {
// 说明是一个普通对象/函数
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
测试代码
var p1 = new MyPromise(function (resolve) {
setTimeout(function () {
resolve(1);
}, 1000);
});
p1.then(function (val) {
console.log(val);
var p3 = new MyPromise(function (resolve) {
setTimeout(function () {
resolve(val + 1);
}, 1000);
});
return p3;
})
.then(function (val) {
console.log(val);
var p4 = new MyPromise(function (resolve) {
setTimeout(function () {
resolve(val + 1);
}, 1000);
});
return p4;
})
.then(function (val) {
console.log(val);
var p4 = new MyPromise(function (resolve) {
setTimeout(function () {
resolve(val + 1);
}, 1000);
});
});
增加 Promise.resolve、catch、race 方法
// 用于promise方法链时 捕获前面onFulfilled/onRejected抛出的异常
MyPromise.catch = function(onRejected) {
return this.then(null, onRejected);
}
MyPromise.resolve = function (value) {
return new Promise(resolve => {
resolve(value);
});
}
MyPromise.race = function(promises) {
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
promise.then(resolve, reject);
});
});
}
MyPromise.allsettle = funciton() {
return new Promsie(reslove, reject) =>{
}
}
Promise.all() 和 Promise.race() 都具有 短路特性
Promise.all(): 如果参数中 promise 有一个失败(rejected),此实例回调失败(reject)
Promise.race():如果参数中某个promise解决或拒绝,返回的 promise就会解决或拒绝。
最后更新于
这有帮助吗?