This article mainly introduces the usage of understanding javascript async. The editor thinks it is quite good. Now I will share it with you and give it as a reference. Let’s follow the editor to take a look
Written in front
This article will implement an optimal method for sequentially reading files. The implementation method starts from the oldest The callback method to the current async, I will also share with you my understanding of the thunk library and the co library. The effect achieved: read a.txt and b.txt sequentially, and concatenate the read contents into a string.
Synchronous reading
const readTwoFile = () => { const f1 = fs.readFileSync('./a.txt'), f2 = fs.readFileSync('./b.txt'); return Buffer.concat([f1, f2]).toString(); };
This method is the most conducive to our understanding, the code is also very clear, without too much The nesting is very easy to maintain, but this has the biggest problem, that is, performance. What node advocates is asynchronous I/O to handle intensive I/O, and synchronous reading is wasteful to a large extent. Server CPU, the disadvantages of this method obviously outweigh the advantages, so just pass it. (In fact, the goal of any asynchronous programming solution in node is to achieve synchronous semantics and asynchronous execution.)
Use callbacks to read
const readTwoFile = () => { let str = null; fs.readFile('./a.txt', (err, data) => { if (err) throw new Error(err); str = data; fs.readFile('./b.txt', (err, data) => { if (err) throw new Error(err); str = Buffer.concat([str, data]).toString(); }); }); };
Using the callback method, it is very simple to implement. Just nest it directly. However, in this case, it is easy to cause a situation that is difficult to maintain and difficult to understand. The most extreme The situation is callback hell.
Promise implementation
const readFile = file => new Promise((reslove, reject) => { fs.readFile(file, (err, data) => { if (err) reject(err); reslove(data); }); }); const readTwoFile = () => { let bf = null; readFile('./a.txt') .then( data => { bf = data; return readFile('./b.txt'); }, err => { throw new Error(err) } ) .then( data => { console.log(Buffer.concat([bf, data]).toString()) }, err => { throw new Error(err) } ); };
Promise can convert horizontal growth callbacks into vertical growth, which can solve some problems. But the problem caused by Promise is code redundancy. At first glance, it is all then, which is not very comfortable, but compared with callback function nesting, it has been greatly improved.
yield
Generator is found in many languages. It is essentially a coroutine. Let’s take a look at the differences and connections between coroutines, threads, and processes. :
Process: The basic unit of resource allocation in the operating system
Thread: The basic unit of resource scheduling in the operating system
Coroutine: an execution unit smaller than a thread, with its own CPU context, one coroutine and one stack
There may be multiple threads in a process. There may be multiple coroutines in a thread. The switching of processes and threads is controlled by the operating system, while the switching of coroutines is controlled by the programmer himself. Asynchronous I/O uses callbacks to deal with intensive I/O. You can also use coroutines to deal with it. Switching coroutines does not waste a lot of resources. Write an I/O operation into a coroutine, and proceed like this During I/O, you can give up the CPU to other coroutines.
js also supports coroutines, which is yield. The intuitive feeling that using yield gives us is that the execution stops at this place and other code continues to run. When you want it to continue execution, it will continue to execute.
function *readTwoFile() { const f1 = yield readFile('./a.txt'); const f2 = yield readFile('./b.txt'); return Buffer.concat([f1, f2]).toString(); }
The sequential reading under yield is also a sequential reading method. There are two different implementation methods for readFile,
Use thunkify
const thunkify = (fn, ctx) => (...items) => (done) => { ctx = ctx || null; let called = false; items.push((...args) => { if (called) return void 0; called = true; done.apply(ctx, args); }); try { fn.apply(ctx, items); } catch(err) { done(err); } };
The thunkify function is a kind of currying idea. The last parameter passed in is the callback function. It can be easily done using thunkify. Implement the automated process of yield function:
const run = fn => { const gen = fn(); let res; (function next(err, data) { let g = gen.next(data); if (g.done) return void 0; g.value(next); })(); };
Use Promise
##
const readFile = file => new Promise((reslove, reject) => { fs.readFile(file, (err, data) => { if (err) reject(err); reslove(data); }); }); const run = fn => { const gen = fn(); let str = null; (function next(err, data) { let res = gen.next(data); if (res.done) return void 0; res.value.then( data => { next(null, data); }, err => { throw new Error(err); } ); })(); }; run(readTwoFile);
// readTwoFile的实现与上面类似,readFile既可以利用Promise也可以利用thunkify // co库返回一个Promise对象 co(readTwoFile).then(data => console.log(data));
const baseHandle = handle => res => { let ret; try { ret = gen[handle](res); } catch(e) { reject(e); } next(ret); }; function co(gen) { const ctx = this, args = Array.prototype.slice.call(arguments, 1); return new Promise((reslove, reject) => { if (typeof gen === 'function') gen = gen.apply(ctx, args); if (!gen || typeof gen.next !== 'function') return resolve(gen); const onFulfilled = baseHandle('next'), onRejected = baseHandle('throw'); onFulfilled(); function next(ret) { if (ret.done) reslove(ret.value); // 将yield的返回值转换为Proimse const value = toPromise.call(ctx, ret.value); if (value && isPromise(value)) return value.then(onFulfilled, onRejected); return onRejected(new TypeError('yield type error')); } }); }
// 把thunkify之后的函数转化为Promise的形式 function thunkToPromise(fn) { const ctx = this; return new Promise(function (resolve, reject) { fn.call(ctx, function (err, res) { if (err) return reject(err); if (arguments.length > 2) res = slice.call(arguments, 1); resolve(res); }); }); }
Ultimate Solution
const readFile = file => new Promise((reslove, reject) => { fs.readFile(file, (err, data) => { if (err) reject(err); reslove(data); }); }); const readTwoFile = async function() { const f1 = await readFile('./a.txt'); const f2 = await readFile('./b.txt'); return Buffer.concat([f1, f2]).toString(); }; readTwoFile().then(data => { console.log(data); });
The above is the detailed content of Detailed explanation of the usage of async in javascript. For more information, please follow other related articles on the PHP Chinese website!