请注意,本文编写于 242 天前,最后修改于 241 天前,其中某些信息可能已经过时。
单线程是JavaScript的程序运行方式
JS 对异步编程的支持方式分为三个阶段(也就是异步的发展历程):
异步编程技术的主要使用场景:
function countdown() {
let i;
console.log("Countdown:");
for (i = 5; i >=0 ; i--) {
setTimeout(function() {
console.log(i === 0 ? "GO!" : i);
}, (5 - i) * 1000);
}
}
countdown();
function countdown() {
console.log("Countdown:");
for (let i = 5; i >= 0; i--) {
setTimeout(function() {
console.log(i === 0 ? "GO!" : i);
}, (5 - i) * 1000);
}
}
countdown();
仔细观察以上两段代码,唯一不同的地方就是关于 i 的声明位置,但是两段代码的执行结果是完全不同的。
原因如下:
因此要牢牢记住这句话:注意回调函数的作用域,回调函数可以访问闭包内的所有内容。
const fs = require('fs');
const fname = 'abc.txt';
fs.readFile(fname, (err, data) => {
if (err) return console.error('error reading file');
console.log(`${fname} contents: ${data}`);
})
回调函数中,所要做的第一件事就是判断 err 是否为真!然后再 do something
如果在写一个使用回调的接口,强烈建议坚持遵守错误优先的约定!
const fs = require('fs');
fs.readFile('a.txt', (err, dataA) => {
if (err) console.error(err);
fs.readFile('b.txt', (err, dataB) => {
if(err) console.error(err);
fs.readFile('c.txt', (err, dataC) => {
if(err) console.error(err);
setTimeout(() => {
fs.writeFile('d.txt', dataA + dataB + dataC, (err) => {
if (err) console.error(err);
});
}, 60 * 1000);
});
});
});
回调嵌套的太多导致思路混乱不清晰,如果再加上一些 try catch 语句的话,就会更加爆炸
function readSketchyFile() {
try {
fs.readFile('abc.txt', (err, data) => {
if (err) throw err;
});
} catch(err) {
console.log('something bad happened')
}
}
try catch 只会捕捉同一个函数内(在本例中是 readSketchyFile
)的异常,而异常的抛出确是在匿名回调函数中产生的,因此程序运行仍然会崩溃。
创建 promise
function launch() {
return new Promise((resolve, reject) => {
if (true) return;
console.log('Lift off');
setTimeout(() => {
resolve('In orbit!')
}, 2 * 1000)
})
}
使用 promise
launch()
.then(() => {
console.log('resolve 的结果');
})
.catch(() => {
console.log('reject 的结果');
});
const EventEmitter = require('events').EventEmitter;
class Countdown extends EventEmitter {
constructor(seconds, superstitious) {
super();
this.seconds = seconds;
this.superstitious = !!superstitious;
}
go() {
const countdown = this;
const timeoutIds = [];
return new Promise((resolve, reject) => {
for (let i = countdown.seconds; i >= 0; i--) {
timeoutIds.push(setTimeout(() => {
if (countdown.superstitious && i === 13) {
timeoutIds.forEach(clearTimeout);
return reject(new Error("DEFINITELY NOT COUNTING THAT"))
}
countdown.emit('tick', i);
if (i === 0) {
resolve();
};
}, (countdown.seconds - i) * 1000));
}
});
}
}
function launch() {
return new Promise((resolve, reject) => {
if (Math.random() < 0.5) return;
console.log('Lift off');
setTimeout(() => {
resolve('In orbit!')
}, 2 * 1000)
})
}
function addTimeout(fn, timeout) {
if (timeout === undefined) timeout = 1000;
return () => {
return new Promise((resolve, reject) => {
const tid = setTimeout(reject, timeout, new Error('Promise timed out'));
fn()
.then(() => {
clearTimeout(tid);
resolve();
})
.catch(() => {
clearTimeout(tid);
reject();
});
});
}
}
const c = new Countdown(5)
.on('tick', i => console.log(i + '...'));
c.go()
.then(addTimeout(launch, 4 * 1000))
.then((msg) => {
console.log(msg);
})
.catch((err) => {
console.error('Houston, we have a problem' + err.message);
});
当一个promise被满足时,可以立即调用另一个返回promise的函数,不用在每一步都捕捉错误
需要注意的是:
promise可以简化异步代码,同时确保回调函数不被多次调用,但却不能避免那些因为promise没有被处理而产生的问题( 既没有调用 resolve, 也没有调用 reject )
function launch() {
return new Promise((resolve, reject) => {
if (Math.random() < 0.5) return;
console.log('Lift off');
setTimeout(() => {
resolve('In orbit!')
}, 2 * 1000)
})
}
只有在 随机数 >= 0.5 时 resolve 才被调用
解决办法:给 promise 一个特定的超时,在以上代码中可以得到展示。
try {
setTimeout(() => {
throw new Error('FAIL');
}, 1000);
} catch(err) {
console.log(err);
}
上述代码在运行时的错误不会被捕获,已经脱离了 try catch 的上下文环境。
解决办法:
① 异步代码内部捕获错误
setTimeout(() => {
try {
throw new Error("FAIL");
} catch (err) {
console.log(err);
}
})
② 在回调函数内部直接捕获错误
var p1 = () => {
return new Promise((resolve, reject) => {
throw new Error('p1_同步_err'); //代码1
setTimeout(() => {
console.log('p1执行');
resolve(true);
// throw new Error('p1_异步_err'); //代码2
// reject('p1_rej'); //代码3
}, 1000);
});
}
var p2 = () => {
return new Promise((resolve, reject) => {
// throw new Error('p2_同步_err'); //代码4
setTimeout(() => {
console.log('p2执行');
// throw new Error('p2_异步_err'); //代码5
// reject('p2_rej'); //代码6
}, 1000);
});
}
p1()
.then()
.catch((err) => {
console.log('我错了' + err);
})
resolve();
语句失效async function doSomething() {
return new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error("fail");
}, 1000);
});
}
async function main() {
try {
await doSomething();
} catch (e) {
console.log(e.message);
}
}
main();
全部评论 (暂无评论)
info 还没有任何评论,你来说两句呐!